!standard 4.2(9) 19-03-19 AI12-0249-1/07 !standard 4.2.1(0) !standard 4.9(3) !class Amendment 18-01-22 !status Amendment 1-2012 18-11-29 !status ARG Approved 10-0-0 18-10-22 !status work item 18-01-22 !status received 15-03-20 !priority Low !difficulty Medium !subject User-defined numeric literals !summary A mechanism for giving a user-defined meaning to numeric literals is added to Ada. !problem As we see an increasing interest in abstract data types such as containers, unbounded strings, "bignums", etc., the inability to use literals with these types becomes more evident. In some cases, an operator such as unary "+" can be "hijacked" to provide for literals, but it always feels to be a bit of a kludge, and may require some kind of "use" clause for the operator to be visible. Literals are currently usable, for the types that support them, without "use" clauses, and it would be great if they could be used as easily with abstract data types as with certain special classes of types where they are now allowed. !proposal Aspects will be defined to allow the lexical numeric literals to be defined to be used with a (non-numeric) type. The aspect identifies a subprogram to be used to interpret the literal as a value of the type. Numeric literals have a "universal" type, which then according to the overload resolution rules in RM 8.6, require the expected type to be a single type or allow the expected type to be any type in a class, so long as the universal type of the literal "covers" the specified class. Since no types currently have literal aspects, there is no immediate upward compatibility issue. However, if we decide to add literal aspects to any existing language-defined types (such as Unbounded_String), we will be potentially creating ambiguities. We need to decide how to represent the literals, and how to convert them. For numeric literals, we could choose a string representation, or some kind of Long_Long_Integer or Long_Long_Real. So long as all conversions of literals are propagated to a library-unit elaboration procedure for library-level types, and to the point of declaration of the type for nested types, the overhead of converting literals can be kept manageable. One might often hope the conversion routines could be evaluated statically, but clearly we are getting into compiler-specific optimizations. Because one use for numeric literals would be for extended precision types, it seems wiser to represent a numeric literal as a string, and require the type to provide a conversion function from string to the type. As mentioned above, the overhead of conversion can hopefully be reduced by having the compiler "hoist" the literals to a point where the conversions are only performed at most once at run-time, and ideally at compile or link time. Because the conversion might fail, we need to specify that it is a bounded error if a literal conversion function propagates an exception, with consequences being a compile-time or link-time error, or a Program_Error or the exception propagated by the conversion function, raised at the point where the value of the literal is used. !wording Modify paragraph 4.2(9): {If its expected type is a numeric type, the}[The] evaluation of a numeric literal[, or the literal null,] yields the represented value. {In other cases, the effect of evaluating a numeric literal is determined by the Integer_Literal or Real_Literal aspect that applies (see 4.2.1). The evaluation of the literal null yields the null value of the expected type.} Add subclause 4.2.1 User-Defined Literals Using one or more of the aspects defined below, a type may be specified to allow the use of one or more kinds of literals as values of the type. Static Semantics The following nonoverridable, type-related operational aspects may be specified for any type T: Integer_Literal This aspect is specified by a /function_/name that denotes a primitive function of T with one parameter of type String and a result type of T. Real_Literal This aspect is specified by a /function_/name that denotes a primitive function of T with one parameter of type String and a result type of T. Legality Rules The Integer_Literal or Real_Literal aspect shall not be specified for a type T if the full view of T is a numeric type. In addition to the places where Legality Rules normally apply (see 12.3), these rules also apply in the private part of an instance of a generic unit. Dynamic Semantics For the evaluation of an integer literal with expected type having an Integer_Literal aspect specified, the value is the result of a call on the function specified by the aspect, with the parameter being a String with lower bound one whose value corresponds to the textual representation of the integer literal. Similarly, the evaluation of a real literal with expected type having a Real_Literal aspect specified, the value is the result of a call on the function specified by the aspect, with the parameter being a String with lower bound one whose value corresponds to the textual representation of the real literal. Bounded Errors It is a bounded error if the evaluation of a literal with expected type having a corresponding _Literal aspect specified, propagates an exception. The possible effect is that an error is reported prior to run time, or Program_Error or the exception propagated by the evaluation is raised at the point of use of the value of the literal. AARM Implementation Note: As always, an implementation may apply "as-if" optimizations (those that result in the same external effects in the absence of erroneous execution) to the function calls associated with user-defined literals. In particular, if the function associated with a _Literal aspect has a Global aspect that indicates no references to global variables, then a number of optimizations are available to the implementation: * The implementation can evaluate a user-defined literal function at compile-time if it has access to the body of the function (for example, if it is inlined), and that body doesn't reference anything evaluated at runtime. If the compile-time evaluation results in an exception, this bounded error allows the compilation unit to be rejected. * Implementations can use existing permissions (see 6.1.2) to avoid evaluating the function associated with a user-defined literal more than once for a particular literal value. This evaluation can be "hoisted" (done once per compilation unit during the elaboration of the unit) if the compiler can prove that the function doesn't depend on any constants or locals with a runtime value not yet elaborated. * If the literal value is not needed by the execution of the program, the function call can be omitted even if it might have side-effects (again, see 6.1.2). Modify 4.9(3) and the following AARM note: * a numeric_literal {of a numeric type}; AARM Ramification: A numeric_literal {of a numeric type} is always a static expression, even if its expected type is not that of a static subtype. However, if its value is explicitly converted to, or qualified by, a nonstatic subtype, the resulting expression is nonstatic. {Non-numeric types can have numeric literals if aspect Integer_Literal or Real_Literal is used; these are never static.} !discussion The !proposal section includes a relatively complete discussion of the issues. But here are a few other interesting questions or issues: * Should numeric literals always be non-negative, with any preceding "-" only usable if there is a visible "-" operator? Probably; it would be somewhat weird if "normal" numeric literals used the type's "-" operator, while a type supporting user-defined literals bypassed the "-" operator. * We do not want to have a situation where a literal may be usable on a partial view but not on the full view, for example, because the full view is a type that already has meaning for the same sort of literal. This avoids problems with full conformance checking when the meaning of a literal might be different in the visible and private parts of a package. * More generally, we chose to allow user-defined literals on almost any sort of type, so long as that sort of type didn't already allow that sort of literal. The thought was that you could imagine a floating point type that would allow the use of string literals for certain special values like "NaN" or "+inf". For the purposes of this Legality Rule, we treated all numeric literals as one "kind" of literal, and disallow any numeric type from having user-defined numeric literals. Having the presence or absence of a "." seemed too subtle to trigger a completely different effect when the expected type was a numeric type. * For other purposes than the Legality Rule noted above, we have two kinds of numeric literals to match the lexical rules of the language, and to allow compile-time rejection of the "wrong" kind of numeric literal when both are not desired. For instance, a Big Integer package probably does not want to allow real literals (that is, literals with decimal points). Leaving this detection to runtime (meaning that an exception may be raised) increases the chance that a mistake in a little-used piece of code could cause a failure. * We have decided not to use this feature for existing language-defined packages (such as Unbounded_Strings). We believe it would be safer to define (in some future standard) a new package and move the original Unbounded_Strings package to Annex J. !examples type Big_Integer is private with Integer_Literal => Big_Integer_Value; function Big_Integer_Value (S : String) return Big_Integer; ... Y : Big_Integer := -3; -- Equivalent to: -- Y : Big_Integer := - Big_Integer_Value ("3"); !corrigendum 4.2(9) @drepl The evaluation of a numeric literal, or the literal @b, yields the represented value. @dby If its expected type is a numeric type, the evaluation of a numeric literal yields the represented value. In other cases, the effect of evaluating a numeric literal is determined by the Integer_Literal or Real_Literal aspect that applies (see 4.2.1). The evaluation of the literal @b yields the null value of the expected type. !corrigendum 4.2.1(0) @dinsc Using one or more of the aspects defined below, a type may be specified to allow the use of one or more kinds of literals as values of the type. @s8<@i> The following nonoverridable, type-related operational aspects may be specified for any type @i: @xhang<@xterm This aspect is specified by a @i@fa that denotes a primitive function of @i with one parameter of type String and a result type of @i.> @xhang<@xterm This aspect is specified by a @i@fa that denotes a primitive function of @i with one parameter of type String and a result type of @i.> @s8<@i> The Integer_Literal or Real_Literal aspect shall not be specified for a type T if the full view of T is a numeric type. In addition to the places where Legality Rules normally apply (see 12.3), these rules also apply in the private part of an instance of a generic unit. @s8<@i> For the evaluation of an integer literal with expected type having an Integer_Literal aspect specified, the value is the result of a call on the function specified by the aspect, with the parameter being a String with lower bound one whose value corresponds to the textual representation of the integer literal. Similarly, the evaluation of a real literal with expected type having a Real_Literal aspect specified, the value is the result of a call on the function specified by the aspect, with the parameter being a String with lower bound one whose value corresponds to the textual representation of the or real literal. @s8<@i> It is a bounded error if the evaluation of a literal with expected type having a corresponding _Literal aspect specified, propagates an exception. The possible effect is that an error is reported prior to run time, or Program_Error or the exception propagated by the evaluation is raised at the point of use of the value of the literal. !corrigendum 4.9(3) @drepl @xbullet;> @dby @xbullet of a numeric type;> !ASIS [Not sure. Might need new aspect names, but I didn't check - Editor.] !ACATS test ACATS B and C-Tests are needed to check that the new capabilities are supported, and that error cases are detected. !appendix From: Tucker Taft Sent: Sunday, January 21, 2018 3:08 PM As requested, here is an AI on user-defined literals. [This is version /01 of the AI - Ed.] The basic idea is that you can specify an aspect (or aspects) on a type to enable it to use literals. The aspects are named Integer_Literal, Real_Literal, Character_Literal, String_Literal, and Null_Literal, to correspond to the 5 syntactically-distinct kinds of literals. Non-character enumeration literals are being ignored for this purpose, since parameterless primitive functions are essentially equivalent, so don't need a special aspect. **************************************************************** From: Steve Baird Sent: Monday, January 22, 2018 3:46 PM > !wording In general, this looks good. I think it is the right approach. We need to modify 4.9 to say that certain literals are not static expressions, right? > Modify paragraph 4.2(11): > For the evaluation of a string_literal of type T, {if its expected type > is a one-dimensional array type with a component subtype that is a > constrained subtype of a character type,} a check is made that the > value of each character of the string_literal belongs to the component > subtype of T. For the evaluation of a null string literal, a check is > made that its lower bound is greater than the lower bound of the base > range of the index type. The exception Constraint_Error is raised if > either of these checks fails. Do the changes made in this paragraph apply properly in the null string literal case? This seems to be saying that the usual rules for determining the bounds of a null string literal apply even in the case where the null string literal maps to a function call. > Implementation Permissions > For a literal with an expected type having a corresponding _Literal > aspect specified, the implementation is permitted to evaluate the > literal at compile time, or at run time at any point after the > freezing point of the expected type, and prior to the first use of the > value of the literal. Furthermore, if two such literals with the same > expected type have identical textual representations, the result of > evaluating one of the literals may be used as the value for both literals. I don't think we want to give permission to *introduce* an ABE failure. If the canonical evaluation point would be after the elaboration of the function body, then clearly we don't want to allow evaluation to precede the elaboration of that body. But suppose the function calls other subprograms, or reads "constant after elaboration" state? I don't think we ever give permission to evaluate something at compile-time; that follows from the usual as-if rules. Do we want to somehow reference the existing rules for a pair of pure function calls with the same arguments (10.2.1(18/3))? I think the Implementation Permissions section needs some work (but I agree with the general intent). > Bounded Errors > It is a bounded error if the evaluation of a literal with expected > type having a corresponding _Literal aspect specified, propagates an > exception. The possible effect is that an error is reported prior to > run time, Program_Error is raised at some point prior to any use of > the value of the literal, or the exception propagated by the > evaluation is raised at the point of use of the value of the literal. Allowing an exception to be raised at some arbitrary point (e.g., during an abort-deferred operation) seems like a bad idea. It reminds me of the state of affairs before per-object constraints were invented. Given something like type T (D : Integer) is private; subtype S is T (-10); ... private ... type T (D : Integer) is record F : String (D .. 10); ...; end record; ... , there was a constraint check associated with S that could fail anywhere in some large region of text. That was a bad idea then (which is why per-object constraints were invented) and it remains a bad idea today. > * A literal may be usable on a partial view but not on the full view, > if the full view is a type that already has meaning for the same > sort of literal. That seems OK. Where is that defined? In a case like type T (<>) is private with String_Literal => Convert; function Convert (X : Wide_Wide_String) return T; procedure Foo (X : T := "abc"); private type T is new Wide_Wide_String; function Convert (X : Wide_Wide_String) return T is (">>>" & T (X)); procedure Foo (X : T := "abc") is null; I like the idea that the second "abc" is illegal . Otherwise we'd need to modify the conformance rules to know about cases like this. **************************************************************** From: Randy Brukardt Sent: Monday, January 22, 2018 11:59 PM > > !wording > In general, this looks good. I think it is the right approach. I agree. BTW, there are some additional issues not noted by Steve mentioned at the bottom of this message. > We need to modify 4.9 to say that certain literals are not static > expressions, right? Certainly. > > Modify paragraph 4.2(11): > > For the evaluation of a string_literal of type T, {if its expected type > > is a one-dimensional array type with a component subtype that is a > > constrained subtype of a character type,} a check is made that the > > value of each character of the string_literal belongs to the component > > subtype of T. For the evaluation of a null string literal, a check is > > made that its lower bound is greater than the lower bound of the base > > range of the index type. The exception Constraint_Error is raised if > > either of these checks fails. > > Do the changes made in this paragraph apply properly in the null > string literal case? This seems to be saying that the usual rules for > determining the bounds of a null string literal apply even in the case > where the null string literal maps to a function call. Don't they have to? The function call takes a Wide_Wide_Wide_String parameter and one needs to know the bounds of that. And if those bounds are garbage, we still have to raise Constraint_Error. (Note, however, that the AI on "(null array)" -- AI12-0248-1 since I assigned it a number an hour ago -- eliminates this check, replacing it by a check that the index type has more than one value. Or at least it would had Tucker proposed any wording.) > > Implementation Permissions > > For a literal with an expected type having a corresponding > > _Literal aspect specified, the implementation is permitted to > > evaluate the literal at compile time, or at run time at any point > > after the freezing point of the expected type, and prior to the > > first use of the value of the literal. Furthermore, if two such > > literals with the same expected type have identical textual > > representations, the result of evaluating one of the literals may be > > used as the value for both literals. > > I don't think we want to give permission to *introduce* an ABE failure. > If the canonical evaluation point would be after the elaboration of > the function body, then clearly we don't want to allow evaluation to > precede the elaboration of that body. > But suppose the function calls other subprograms, or reads "constant > after elaboration" state? > > I don't think we ever give permission to evaluate something at > compile-time; that follows from the usual as-if rules. Do we want to > somehow reference the existing rules for a pair of pure function calls > with the same arguments (10.2.1(18/3))? > > I think the Implementation Permissions section needs some work (but I > agree with the general intent). It seems to me that the Pure function rules (and especially the corresponding ones for functions with Global => null -- I hope these exist, they did in every version that I worked years ago -- we don't have or want Pure_Function as we want Global instead, but then it better work the same) are more appropriate here, along with as-if optimizations, than this rather bizarre permission. Indeed, I think we should get rid of this permission altogether and replace it with an AARM Implementation note that discusses as-if optimizations and the fact that an implementation need only call a pure/Global => null function only once per set of parameters. If the function has side-effects (yikes!), that's madness and the code should simply revert to canonical semantics. Who cares if stupid code is fast? > > Bounded Errors > > It is a bounded error if the evaluation of a literal with > > expected type having a corresponding _Literal aspect specified, > > propagates an exception. The possible effect is that an error is > > reported prior to run time, Program_Error is raised at some point > > prior to any use of the value of the literal, or the exception > > propagated by the evaluation is raised at the point of use of the value of > > the literal. > > Allowing an exception to be raised at some arbitrary point (e.g., > during an abort-deferred operation) seems like a bad idea. Agreed. > It reminds me of the state of affairs before per-object constraints > were invented. Given something like > > type T (D : Integer) is private; > subtype S is T (-10); > ... > private > ... > type T (D : Integer) is > record F : String (D .. 10); ...; end record; > ... > > , there was a constraint check associated with S that could fail > anywhere in some large region of text. That was a bad idea then (which > is why per-object constraints were invented) and it remains a bad idea > today. If we had exception contracts (hint, hint), this would be a non-problem as a compiler could optimize this call IFF no exceptions can be raised. Otherwise, canonical semantics should hold. I don't see any way to reconcile the desire to "hoist" literal functions with these side-cases. And I don't really see the point; this is just a function call like any other function call. If it is simple enough, it probably will get auto-inlined and you are saving essentially nothing with this permission and bounded error. Or extra calls can be eliminated using the Pure/Global permissions noted earlier. This only seems to improve the performance of weird (side-effects) or buggy (unexpected exceptions) literal routines. And that seems to be exactly the cases we don't care about. So this smacks of premature optimization, especially as it only helps one specific corner case (as an implementer, I'm much more interested in things that will speed up a substantial fraction of functions). > > * A literal may be usable on a partial view but not on the full view, > > if the full view is a type that already has meaning for the same > > sort of literal. That seems OK. > > Where is that defined? In a case like > > type T (<>) is private with String_Literal => Convert; > function Convert (X : Wide_Wide_String) return T; > procedure Foo (X : T := "abc"); > private > type T is new Wide_Wide_String; > function Convert (X : Wide_Wide_String) return T is (">>>" & T (X)); > procedure Foo (X : T := "abc") is null; > > I like the idea that the second "abc" is illegal . Otherwise we'd need > to modify the conformance rules to know about cases like this. It's defined by the way that he wrote the interpretation of literals; you check for the existing cases first, and only then (if the existing cases don't work) do you use the aspect specification. I don't think the wording is very clear about that. Note that this should be just hiding the specified literal with the language-defined literal meaning. If you can give a literal for a private type, it can always be given for the full type (but it might not mean exactly the same thing). In any case, you are right that the conformance rules need to be modified. If someone defines these using a goofy function with side-effects, you'll need to reject conformance anyway: > type T (<>) is private with String_Literal => Convert; > function Convert (X : Wide_Wide_String) return T; > procedure Foo (X : T := "abc"); > private > type T is new Float; > function Convert (X : Wide_Wide_String) return T is (Random); > procedure Foo (X : T := "abc") is null; Each evaluation of "abc" gives a different value of T, so argubly the two Foos don't conform. And even if we decided to ignore this (perhaps we ought to -- here's a place where a Bounded Error makes sense), we still need to detect the case noted previously where the outer and inner literals use different rules. --- >Modify paragraph 3.6.3(1): > A one-dimensional array type whose component type is a character type > is called a /string/ type{, as is a type with a specified String_Literal > aspect (see 4.2.1)}. Umm, don't "string types" have a bunch of extra semantics beyond just literals? In particular, string types can be static. Doesn't this cause some problems? There is a similar comment about "character types" (wording not shown). --- > Modify paragraph 4.2(9): > {If its expected type is a numeric type, t}[T]he evaluation of a > numeric literal[, or the literal null,] yields the represented value. > {If its expected type is an access type, the evaluation of the > literal null yields the null value of the expected type.} In other > cases, the effect of evaluating a numeric or null literal is > determined by the Integer_Literal, Real_Literal, or Null_Literal > aspect that applies (see 4.2.1). Something is wrong with the Insertion/Deletion marks here, as I'm pretty sure that text involving the Null_Literal aspect is not existing text. :-) --- >Modify paragraph 4.2(10): > The evaluation of a string_literal that is a primary {and has an > expected type that is a one-dimensional array type with a character > type as its component type,} yields an array value containing the value > of each character of the sequence of characters of the string_literal, > as defined in 2.6. The bounds of this array value are determined > according to the rules for positional_array_aggregates (see 4.3.3 ), > except that for a null string literal, the upper bound is the > predecessor of the lower bound. Don't we need these same rules for the bounds of the object that gets passed into the function specified by the String_Literal aspect? That object needs defined bounds just as much as the direct uses! The function that is called is just a normal Ada function after all, someone could reasonably query Val'First. --- >* If we go with this proposal, we will have to decide whether to use > this feature for existing language-defined packages > (e.g. Unbounded_Strings), and if so, whether certain existing > functions should be eliminated from the packages (e.g. overloadings > of "&" and the comparison operators that have String as one of > the operand types). At this stage it might be safer to define a > new package and move the original Unbounded_Strings package to > Annex J. The reason that we did not make such a proposal for literals in Ada 2012 (we discussed it briefly) was that it couldn't be used in Ada.String.Unbounded. I think the only way would be to replace the package. One reason to do so is to come up with an overriding string abstraction, I made an attempt at it in AI12-0021-1. I note that is suspiciously similar to this idea, as it uses Wide_Wide_String as an intermediary to support conversions between arbitrary string types. The proposal even used an imaginary "String_Literal" aspect! If one had such a package, then you'd want to replace all of the existing string packages with class-wide ones that would allow storing any string type. (That would make unbounded-ness orthogonal to the representation of the actual string; you'd probably implement the storage as an array of stream elements.) That would give a good excuse to introduce proper literals. (We'd also be able to make UTF-8 and the like into proper types, and allow any of those to represent file names and other I/O strings -- it would mean the end of Wide_Wide_Wide_Wide_ Madness.) > Alternatively, we could define some kind of "preference" > for or against user-defined interpretation of literals, though we > know that "Beaujolais" effects are lurking around the corner when > you set up preference rules. We already have some preference rules > dealing with "root" numeric types, and these could be seen as similar, > but the root-numeric-type preference avoids Beaujolais effects because > the preference is for operations that are always visible. > So barring some clear proof of being Beaujolais-effect-free, we should > probably steer clear of a preference rule. Umm, no thanks. Root numeric preference is a mess in our compiler, and I have no interest in trying to figure out something like it for other things. **************************************************************** From: Steve Baird Sent: Tuesday, January 23, 2018 12:45 PM >>> Modify paragraph 4.2(11): >>> For the evaluation of a string_literal of type T, {if its >>> expected > type >>> is a one-dimensional array type with a component subtype that is a >>> constrained subtype of a character type,} a check is made that the >>> value of each character of the string_literal belongs to the component >>> subtype of T. For the evaluation of a null string literal, a check is >>> made that its lower bound is greater than the lower bound of the base >>> range of the index type. The exception Constraint_Error is raised if >>> either of these checks fails. >> >> Do the changes made in this paragraph apply properly in the null >> string literal case? This seems to be saying that the usual rules for >> determining the bounds of a null string literal apply even in the >> case where the null string literal maps to a function call. > > Don't they have to? The function call takes a Wide_Wide_Wide_String > parameter and one needs to know the bounds of that. And if those > bounds are garbage, we still have to raise Constraint_Error. (Note, > however, that the AI on "(null array)" -- AI12-0248-1 since I assigned > it a number an hour ago > -- eliminates this check, replacing it by a check that the index type > has more than one value. Or at least it would had Tucker proposed any > wording.) Given a null string literal whose evaluation is going to involve passing a wide_wide_string to a function, what is the case you are talking about where the bounds of the wide_wide_string might be bad (or, for that matter, anything other than 1 .. 0)? There are two separate sets of bounds here - the bounds of the Wide_Wide_String that is passed to the function and (in the case where the type of the literal is an array type) the bounds of the function result. Only the former need to be discussed in this AI (the latter follow from the usual rules about function results). In the phrase "its lower bound", the "it" refers to the value of the literal, not to the intermediate Wide_Wide_String value. This makes no sense, but it also wouldn't make any sense if it did refer to the intermediate Wide_Wide_String value. [Incidentally, I argue later on in this message that we don't need to talk about the bounds of the Wide_Wide_String value at all because all the dynamic semantics stuff either does or should follow from an equivalence rule.] > Indeed, I think we should get rid of this permission altogether and > replace it with an AARM Implementation note that discusses as-if > optimizations and the fact that an implementation need only call a > pure/Global => null function only once per set of parameters. If the > function has side-effects (yikes!), that's madness and the code should > simply revert to canonical semantics. Who cares if stupid code is fast? I agree that we don't care much about functions that have side effects, or about functions which are called twice with the same arguments and return different results. Behavior in these cases needs to be defined, but these are silly cases. The case I'm more concerned about is a function that reads (but does not write) variable state. In particular, I thinking of variable state that is updated at some point and never subsequently modified. If all calls to the function occur after the state is updated, then the state is effectively (for purposes of our function) constant and our function then has the desired pure-ish properties (no side effects and two calls with same arguments yield same results). But if an implementation permission allows evaluation of the function call before the state update occurs, then problems result. ABE checks can be viewed as a special case of this if you choose to think of there being a (notional) Boolean flag associated with each subprogram indicating whether the body has been elaborated. The elaboration of the body then has the effect of modifying the flag and a call which depends on the value of this flag can be thought of as reading variable state. And of course a function which is subject to its own ABE check is hardly a pathological corner case. > I don't see any way to reconcile the desire to "hoist" literal > functions with these side-cases. And I don't really see the point; > this is just a function call like any other function call. Agreed. > This only seems to improve the performance of weird (side-effects) or > buggy (unexpected exceptions) literal routines. I disagree, as described above ... > And that seems to be exactly the > cases we don't care about. So this smacks of premature optimization, > especially as it only helps one specific corner case (as an > implementer, I'm much more interested in things that will speed up a > substantial fraction of functions). > ... but I still think your conclusion about hoisting is right. >>> * A literal may be usable on a partial view but not on the full view, >>> if the full view is a type that already has meaning for the same >>> sort of literal. That seems OK. >> >> >> Where is that defined? In a case like >> >> type T (<>) is private with String_Literal => Convert; >> function Convert (X : Wide_Wide_String) return T; >> procedure Foo (X : T := "abc"); >> private >> type T is new Wide_Wide_String; >> function Convert (X : Wide_Wide_String) return T is (">>>" & T (X)); >> procedure Foo (X : T := "abc") is null; >> >> I like the idea that the second "abc" is illegal . Otherwise we'd >> need to modify the conformance rules to know about cases like this. > > It's defined by the way that he wrote the interpretation of literals; > you check for the existing cases first, and only then (if the existing > cases don't work) do you use the aspect specification. I don't think > the wording is very clear about that. > There are 2 ways to go if both mechanisms are available (as in the private part of the above example): either a preference rule is used to choose one interpretation over the other or the literal is illegal. I interpret the statement "A literal may be usable in a partial view but not on the full view, if the full view is a type that already has a meaning for the same sort of literal" to mean that the second option has been selected. I asked for an explanation of how this statement followed from the wording and you (implicitly) agreed with me that you couldn't justify it either (because the first option, not the second, was chosen). > --- > >> Modify paragraph 4.2(10): >> The evaluation of a string_literal that is a primary {and has an >> expected type that is a one-dimensional array type with a character >> type as its component type,} yields an array value containing the value >> of each character of the sequence of characters of the string_literal, >> as defined in 2.6. The bounds of this array value are determined >> according to the rules for positional_array_aggregates (see 4.3.3 ), >> except that for a null string literal, the upper bound is the >> predecessor of the lower bound. > > Don't we need these same rules for the bounds of the object that gets > passed into the function specified by the String_Literal aspect? That > object needs defined bounds just as much as the direct uses! The > function that is called is just a normal Ada function after all, > someone could reasonably query Val'First. > Do we need to mention this dynamic-semantics stuff at all? If this isn't already handled by an equivalence rule, then it should be. T'("abc") is equivalent to T'(T'String_Literal_Function (Wide_String_Param => "abc")) and we don't need to say any more than that about the bounds of the Wide_Wide_String literal. Note that the parameter subtype of the String_Literal function can be constrained. **************************************************************** From: Randy Brukardt Sent: Tuesday, January 23, 2018 3:30 PM ... > > Indeed, I think we should get rid of this permission altogether and > > replace it with an AARM Implementation note that discusses as-if > > optimizations and the fact that an implementation need only call a > > pure/Global => null function only once per set of parameters. If the > > function has side-effects (yikes!), that's madness and the code > > should simply revert to canonical semantics. Who cares if stupid code is fast? > > I agree that we don't care much about functions that have side > effects, or about functions which are called twice with the same > arguments and return different results. Behavior in these cases needs > to be defined, but these are silly cases. > > The case I'm more concerned about is a function that reads (but does > not write) variable state. In particular, I thinking of variable state > that is updated at some point and never subsequently modified. > If all calls to the function occur after the state is updated, then > the state is effectively (for purposes of our > function) constant and our function then has the desired pure-ish > properties (no side effects and two calls with same arguments yield > same results). > But if an implementation permission allows evaluation of the function > call before the state update occurs, then problems result. > > ABE checks can be viewed as a special case of this if you choose to > think of there being a (notional) Boolean flag associated with each > subprogram indicating whether the body has been elaborated. The > elaboration of the body then has the effect of modifying the flag and > a call which depends on the value of this flag can be thought of as > reading variable state. This is exactly how ABE is implemented in Janus/Ada; nothing "notional" about it. :-) > And of course a function which is subject to its own ABE check is > hardly a pathological corner case. Right, but such a function is still Global => null; nothing about the Pure permission allows ABE violations! My point here was that we already have the needed permission in useful cases and the ones that aren't that useful (or safely optimizable) shouldn't be optimized anyway. For unbounded BigNum, evaluating a literal will do a heap allocation. Optimizing that could very well end up with the wrong answer. > > I don't see any way to reconcile the desire to "hoist" literal > > functions with these side-cases. And I don't really see the point; > > this is just a function call like any other function call. > > Agreed. > > > This only seems to improve the performance of weird (side-effects) > > or buggy (unexpected exceptions) literal routines. > > I disagree, as described above ... > > > And that seems to be exactly the > > cases we don't care about. So this smacks of premature optimization, > > especially as it only helps one specific corner case (as an > > implementer, I'm much more interested in things that will speed up a > > substantial fraction of functions). > > ... but I still think your conclusion about hoisting is right. Good. I won't argue with you more if we agree on the conclusion. > >>> * A literal may be usable on a partial view but not on the full view, > >>> if the full view is a type that already has meaning for the same > >>> sort of literal. That seems OK. > >> > >> Where is that defined? In a case like > >> > >> type T (<>) is private with String_Literal => Convert; > >> function Convert (X : Wide_Wide_String) return T; > >> procedure Foo (X : T := "abc"); > >> private > >> type T is new Wide_Wide_String; > >> function Convert (X : Wide_Wide_String) return T is (">>>" & T (X)); > >> procedure Foo (X : T := "abc") is null; > >> > >> I like the idea that the second "abc" is illegal . Otherwise we'd > >> need to modify the conformance rules to know about cases like this. > > > > It's defined by the way that he wrote the interpretation of > > literals; you check for the existing cases first, and only then (if > > the existing cases don't work) do you use the aspect specification. > > I don't think the wording is very clear about that. > > There are 2 ways to go if both mechanisms are available (as in the > private part of the above example): either a preference rule is used > to choose one interpretation over the other or the literal is illegal. > I interpret the statement > "A literal may be usable in a partial view but not on the > full view, if the full view is a type that already has a meaning > for the same sort of literal" > to mean that the second option has been selected. I read it to mean that the _Literal aspect implementation is not available. The literal being illegal is nonsense; how would you write operations on the full type if it is Integer or Float without literals? That would be very limiting as to what the abstraction could be. If that was going to be the case, then I'd suggest instead making the full types themselves illegal if they have conflicting literals. (Almost every private type ends up getting completed with a record type anyway, so the other cases are corner cases anyway.) That way, the programmer would know to wrap the full type in a record before proceeding. > I asked for an explanation of how this statement followed from the > wording and you (implicitly) agreed with me that you couldn't justify > it either (because the first option, not the second, was chosen). The first option has to be chosen, and I read the statement as describing that choice (apparently, not very well). Tucker had better tell us what he meant... > > --- > > > >> Modify paragraph 4.2(10): > >> The evaluation of a string_literal that is a primary {and has an > >> expected type that is a one-dimensional array type with a character > >> type as its component type,} yields an array value containing the value > >> of each character of the sequence of characters of the string_literal, > >> as defined in 2.6. The bounds of this array value are determined > >> according to the rules for positional_array_aggregates (see 4.3.3 ), > >> except that for a null string literal, the upper bound is the > >> predecessor of the lower bound. > > > > Don't we need these same rules for the bounds of the object that > > gets passed into the function specified by the String_Literal > > aspect? That object needs defined bounds just as much as the direct > > uses! The function that is called is just a normal Ada function > > after all, someone could reasonably query Val'First. > > Do we need to mention this dynamic-semantics stuff at all? If this > isn't already handled by an equivalence rule, then it should be. > T'("abc") > is equivalent to > T'(T'String_Literal_Function (Wide_String_Param => > "abc")) and we don't need to say any more than that about the bounds > of the Wide_Wide_String literal. There's no literal equivalence in the wording Tucker sent, but I suppose you could argue that "the value is the result of a call on the function specified by the aspect, with the parameter being the Wide_Wide_String that corresponds to the literal." requires some sort of equivalence. I would want that to be a lot more explicit if you expect to get bounds and the various rules on returns from this call. > Note that the parameter subtype of the String_Literal function can be > constrained. That shouldn't be allowed, as it would allow only one length of literal (and force Constraint_Error for most literals, something we want to avoid for optimization reasons if nothing else.). Note that the same consideration applies to the numeric literal routines. The wording of the rules suggest Tucker meant that the parameter would literally be Wide_Wide_String and nothing else, but I think you are right that a constrained subtype would be allowed. It probably should say "an unconstrained subtype of type Wide_Wide_String" and similarly for the numeric ones. **************************************************************** From: Tucker Taft Sent: Tuesday, January 23, 2018 4:49 PM ... > Don't they have to? The function call takes a Wide_Wide_Wide_String > parameter and one needs to know the bounds of that. And if those > bounds are garbage, we still have to raise Constraint_Error. (Note, > however, that the AI on "(null array)" -- AI12-0248-1 since I assigned > it a number an hour ago > -- eliminates this check, replacing it by a check that the index type > has more than one value. Or at least it would had Tucker proposed any > wording.) Good point, we need bounds for the Wide_Wide_String parameter, so those might as well be determined the same way as a regular string literal. ... > It seems to me that the Pure function rules (and especially the > corresponding ones for functions with Global => null -- I hope these > exist, they did in every version that I worked years ago -- we don't > have or want Pure_Function as we want Global instead, but then it > better work the same) are more appropriate here, along with as-if > optimizations, than this rather bizarre permission. > > Indeed, I think we should get rid of this permission altogether and > replace it with an AARM Implementation note that discusses as-if > optimizations and the fact that an implementation need only call a > pure/Global => null function only once per set of parameters. If the > function has side-effects (yikes!), that's madness and the code should > simply revert to canonical semantics. Who cares if stupid code is fast? OK, I guess I am convinced. We might encourage implementations to report at compile-time any expected run-time failures for literals. And I suppose we might as well allow the exceptions propagated by the conversion to simply propagate, and remove the bounded error completely. Still seems a bit weird for a literal to propagate an exception, rather than being rejected at compile-time. Perhaps we could still give a permission to give a compile-time error if the implementation can determine that the evaluation of a literal will always fail. Which I suppose gets us back into treating it like a bounded error. > ... > > I don't see any way to reconcile the desire to "hoist" literal > functions with these side-cases. And I don't really see the point; > this is just a function call like any other function call. If it is > simple enough, it probably will get auto-inlined and you are saving > essentially nothing with this permission and bounded error. Or extra > calls can be eliminated using the Pure/Global permissions noted earlier. > > This only seems to improve the performance of weird (side-effects) or > buggy (unexpected exceptions) literal routines. And that seems to be > exactly the cases we don't care about. So this smacks of premature > optimization, especially as it only helps one specific corner case (as > an implementer, I'm much more interested in things that will speed up > a substantial fraction of functions). I am not so concerned about performance, as I am about not hearing about bad literals until run-time. In ParaSail, a precondition on the conversion function is the way that you indicate exactly what range of literals, is supported for the type. It turns out in ParaSail every (library-level) function is "pure," and all pure functions with static parameters are evaluated at compile-time, so you find out about bad literals when you would expect, namely at compile-time. So I guess I would argue for some kind of bounded error, to allow implementations to give a compile-time error. But I agree that raising an exception at any point other than the occurrence of the literal is not useful. >>> * A literal may be usable on a partial view but not on the full view, >>> if the full view is a type that already has meaning for the same >>> sort of literal. That seems OK. >> >> >> Where is that defined? In a case like >> >> type T (<>) is private with String_Literal => Convert; >> function Convert (X : Wide_Wide_String) return T; >> procedure Foo (X : T := "abc"); >> private >> type T is new Wide_Wide_String; >> function Convert (X : Wide_Wide_String) return T is (">>>" & T (X)); >> procedure Foo (X : T := "abc") is null; >> >> I like the idea that the second "abc" is illegal . Otherwise we'd >> need to modify the conformance rules to know about cases like this. > > It's defined by the way that he wrote the interpretation of literals; > you check for the existing cases first, and only then (if the existing > cases don't work) do you use the aspect specification. I don't think > the wording is very clear about that. Yes, some clarification is required. But it sounds like there is agreement that when you can see the full view, and it has a particular kind of literal, we want that literal interpreted in the "builtin" way. > Note that this should be just hiding the specified literal with the > language-defined literal meaning. If you can give a literal for a > private type, it can always be given for the full type (but it might > not mean exactly the same thing). > > In any case, you are right that the conformance rules need to be modified. > If someone defines these using a goofy function with side-effects, > you'll need to reject conformance anyway: > >> type T (<>) is private with String_Literal => Convert; >> function Convert (X : Wide_Wide_String) return T; >> procedure Foo (X : T := "abc"); >> private >> type T is new Float; >> function Convert (X : Wide_Wide_String) return T is (Random); >> procedure Foo (X : T := "abc") is null; > > Each evaluation of "abc" gives a different value of T, so argubly the > two Foos don't conform. > > And even if we decided to ignore this (perhaps we ought to -- here's a > place where a Bounded Error makes sense), we still need to detect the > case noted previously where the outer and inner literals use different rules. Yes, we should complain in that (Baird-ian ;-) case. > --- > >> Modify paragraph 3.6.3(1): >> A one-dimensional array type whose component type is a character type >> is called a /string/ type{, as is a type with a specified > String_Literal >> aspect (see 4.2.1)}. > > Umm, don't "string types" have a bunch of extra semantics beyond just > literals? In particular, string types can be static. Doesn't this > cause some problems? Good point, we will have to look at that. > > There is a similar comment about "character types" (wording not shown). > > --- > >> Modify paragraph 4.2(9): >> {If its expected type is a numeric type, t}[T]he evaluation of a >> numeric literal[, or the literal null,] yields the represented value. >> {If its expected type is an access type, the evaluation of the >> literal null yields the null value of the expected type.} In other >> cases, the effect of evaluating a numeric or null literal is >> determined by the Integer_Literal, Real_Literal, or Null_Literal >> aspect that applies (see 4.2.1). > > Something is wrong with the Insertion/Deletion marks here, as I'm > pretty sure that text involving the Null_Literal aspect is not > existing text. :-) Good point. > --- > >> Modify paragraph 4.2(10): >> The evaluation of a string_literal that is a primary {and has an >> expected type that is a one-dimensional array type with a character >> type as its component type,} yields an array value containing the >> value of each character of the sequence of characters of the >> string_literal, as defined in 2.6. The bounds of this array value are >> determined according to the rules for positional_array_aggregates >> (see 4.3.3 ), except that for a null string literal, the upper bound >> is the predecessor of the lower bound. > > Don't we need these same rules for the bounds of the object that gets > passed into the function specified by the String_Literal aspect? That > object needs defined bounds just as much as the direct uses! The > function that is called is just a normal Ada function after all, > someone could reasonably query Val'First. Yes, as mentioned above, I agree we need to define the bounds of the Wide_Wide_String passed into the user's conversion function. > --- > >> * If we go with this proposal, we will have to decide whether to use >> this feature for existing language-defined packages (e.g. >> Unbounded_Strings), and if so, whether certain existing functions >> should be eliminated from the packages (e.g. overloadings of "&" and >> the comparison operators that have String as one of the operand >> types). At this stage it might be safer to define a new package and >> move the original Unbounded_Strings package to Annex J. > > The reason that we did not make such a proposal for literals in Ada > 2012 (we discussed it briefly) was that it couldn't be used in Ada.String.Unbounded. > > I think the only way would be to replace the package. One reason to do > so is to come up with an overriding string abstraction, I made an > attempt at it in AI12-0021-1. I note that is suspiciously similar to > this idea, as it uses Wide_Wide_String as an intermediary to support > conversions between arbitrary string types. The proposal even used an > imaginary "String_Literal" aspect! > > If one had such a package, then you'd want to replace all of the > existing string packages with class-wide ones that would allow storing > any string type. (That would make unbounded-ness orthogonal to the > representation of the actual string; you'd probably implement the > storage as an array of stream elements.) That would give a good excuse > to introduce proper literals. (We'd also be able to make UTF-8 and the > like into proper types, and allow any of those to represent file names > and other I/O strings -- it would mean the end of Wide_Wide_Wide_Wide_ > Madness.) I would want that to be a separate AI in any case. I think this AI will not propose using this feature in the existing String packages. A user could derive from one of these types and add the string literals, presumably. >> Alternatively, we could define some kind of "preference" >> for or against user-defined interpretation of literals, though we >> know that "Beaujolais" effects are lurking around the corner when >> you set up preference rules. We already have some preference rules >> dealing with "root" numeric types, and these could be seen as >> similar, but the root-numeric-type preference avoids Beaujolais >> effects because the preference is for operations that are always visible. >> So barring some clear proof of being Beaujolais-effect-free, we >> should probably steer clear of a preference rule. > > Umm, no thanks. Root numeric preference is a mess in our compiler, > and I have no interest in trying to figure out something like it for other > things. Agreed -- no preference rule. **************************************************************** From: Jean-Pierre Rosen Sent: Tuesday, January 23, 2018 11:26 PM >>>> type T (<>) is private with String_Literal => Convert; >>>> function Convert (X : Wide_Wide_String) return T; >>>> procedure Foo (X : T := "abc"); >>>> private >>>> type T is new Wide_Wide_String; >>>> function Convert (X : Wide_Wide_String) return T is (">>>" & T (X)); >>>> procedure Foo (X : T := "abc") is null; Couldn't we make this illegal (as ambiguous), but require: procedure Foo (X : T := Wide_Wide_String'("abc")) is null; **************************************************************** From: Tucker Taft Sent: Wednesday, January 24, 2018 2:31 AM Here you are implying there is an implicit conversion between Wide_Wide_String and T, and that is not the anticipated semantics of this feature. **************************************************************** From: Jean-Pierre Rosen Sent: Wednesday, January 24, 2018 8:59 AM No, I mean the qualification requires "abc" to be Wide_Wide_String, therefore the assignment triggers the call to the convert function. No ambiguity. **************************************************************** From: Tucker Taft Sent: Wednesday, January 24, 2018 9:10 AM It doesn't look like that to me. It is really weird for me to see "X : T := Y'(blah)" Everywhere else in Ada the type of Y'(blah) is Y, not T. **************************************************************** From: Tucker Taft Sent: Wednesday, January 24, 2018 12:16 PM >> Everywhere else in Ada the type of Y'(blah) is Y, not T. > Yes - that's what implicit conversion is about.... But we certainly are not saying that all Wide_Wide_String expressions are implicitly convertible to all types with a String_Literal aspect. We are only saying that string literals are overloaded on such types. That is quite different. The use of Wide_Wide_String is just as an intermediate in the conversion, but should not be considered as the type of the literal in the source code. **************************************************************** From: Randy Brukardt Sent: Wednesday, November 28, 2018 1:03 AM (1) This AI will be split into three AIs as requested. The string and character versions will build on the rules given in the base AI for numeric literals. (2) The summary is "** TBD". That won't get past editorial review (right, John??). I changed it to "A mechanism for user-defined literals is added." (where is appropriate for the AI). (3) The first line of my meeting notes says that "We need numeric_literals that are non-static, so 4.9(3) needs an update." This wasn't in the AI that Tucker sent, so I changed this to: * a numeric_literal {of a numeric type}; AARM Ramification: A numeric_literal {of a numeric type} is always a static expression, even if its expected type is not that of a static subtype. However, if its value is explicitly converted to, or qualified by, a nonstatic subtype, the resulting expression is nonstatic. {Non-numeric types can have numeric literals if aspect Integer_Literal or Real_Literal is used; these are never static.} I used this wording so as to leave the meaning unchanged in existing cases. (Else I would have used "of a static subtype".) (4) In e-mail, we agreed to limit the Bounded Error to raising exceptions only at the location of a literal. That is not reflected in the Bounded Error wording in the AI. I've corrected the wording for this: It is a bounded error if the evaluation of a literal with expected type having a corresponding _Literal aspect specified, propagates an exception. The possible effect is that an error is reported prior to run time, or Program_Error or the exception propagated by the evaluation is raised at the point of use of the value of the literal. [We always allow Program_Error to be raised by a Bounded Error.] (5) I had pointed out the insertion marks in the change for 4.2(9) are screwed up; but those were not fixed. I've corrected that paragraph as follows: Modify paragraph 4.2(9): {If its expected type is a numeric type, t}[T]he evaluation of a numeric literal[, or the literal null,] yields the represented value. {If its expected type is an access type, the evaluation of the literal null yields the null value of the expected type. In other cases, the effect of evaluating a numeric or null literal is determined by the Integer_Literal, Real_Literal, or Null_Literal aspect that applies (see 4.2.1).} (6) The AI has "Add section 4.2.1 ...". But this is a "subclause", not a "section". Fixed accordingly. **************************************************************** From: Randy Brukardt Sent: Wednesday, November 28, 2018 1:21 AM Pushed send too soon. (7) In e-mail discussion, we had agreed that we needed to define the bounds of the string passed to these routines (for numeric and string literals). There doesn't seem to be any reason to get fancy here, so I just added that the lower bound of that string is one (the upper bound then is obvious without needing discussion). Specifically: For the evaluation of an integer (or real) literal with expected type having an Integer_Literal (or Real_Literal) aspect specified, the value is the result of a call on the function specified by the aspect, with the parameter being a string with lower bound one whose value corresponds to the textual representation of the integer (or real) literal. and For the evaluation of a string_literal with expected type having a String_Literal aspect specified, the value is the result of a call on the function specified by the aspect, with the parameter being the Wide_Wide_String with lower bound one that corresponds to the literal. (8) For the string literal case, the wording defines such types as "string types". I had wondered: > Umm, don't "string types" have a bunch of extra semantics beyond just > literals? In particular, string types can be static. Doesn't this > cause some problems? to which Tucker responded: "Good point, we will have to look at that." But so far as I can tell, no one has. Hopefully someone will do that ASAP. (9) I had suggested eliminating the Implementation Permission completely (replacing it with an Implementation Note suggesting the use of as-if optimizations and applying the permission to call a function with Global => null only once), since it doesn't allow anything that isn't already allowed for any Global => null function. (At least, I've been assuming that there is a rule similar to the one for functions in Pure packages that applies to such functions -- they have similar purposes -- if not, we should fix AI12-0079-1, not this AI!) Tucker had replied that "he was convinced", but the permission remains. We actually discussed it a bit during the meeting, so I don't want to delete it without getting the OK from this list. **************************************************************** From: Tucker Taft Sent: Wednesday, November 28, 2018 10:33 AM > Pushed send too soon. > > (7) In discussion, we had agreed that we needed to define the bounds > of the string passed to these routines (for numeric and string > literals). There doesn't seem to be any reason to get fancy here, so I > just added that the lower bound of that string is one (the upper bound > then is obvious without needing discussion). > > Specifically: > > For the evaluation of an integer (or real) literal with expected type > having an Integer_Literal (or Real_Literal) aspect specified, the > value is the result of a call on the function specified by the aspect, > with the parameter being a string with lower bound one whose value > corresponds to the textual representation of the integer (or real) literal. > > and > > For the evaluation of a string_literal with expected type having a > String_Literal aspect specified, the value is the result of a call on > the function specified by the aspect, with the parameter being the > Wide_Wide_String with lower bound one that corresponds to the literal. The above seems fine. > (8) For the string literal case, the wording defines such types as > "string types". I had wondered: > >> Umm, don't "string types" have a bunch of extra semantics beyond just >> literals? In particular, string types can be static. Doesn't this >> cause some problems? > > to which Tucker responded: > > "Good point, we will have to look at that." > > But so far as I can tell, no one has. Hopefully someone will do that ASAP. "string type" appears in the following places in the RM: 3.6.3 String Types -- whole section 4.2 Literals -- many paragraphs 4.9 Static Expressions and Static Subtypes -- para 26/3 4.2 and 3.6.3 are already handled in this AI. 4.9(26/3) should be modified as follows: A static subtype is either a static scalar subtype or a static string subtype. A static scalar subtype is an unconstrained scalar subtype whose type is not a descendant of a formal type, or a constrained scalar subtype formed by imposing a compatible static constraint on a static scalar subtype. A static string subtype is {a string subtype that does not have a specified String_Literal aspect, and that is} an unconstrained [string]{array} subtype whose index subtype and component subtype are static, or a constrained [string]{array} subtype formed by imposing a compatible static constraint on a static string subtype. In any case, the subtype of a generic formal object of mode in out, and the result subtype of a generic formal function, are not static. Also, a subtype is not static if any Dynamic_Predicate specifications apply to it. > (9) I had suggested eliminating the Implementation Permission > completely (replacing it with an Implementation Note suggesting the > use of as-if optimizations and applying the permission to call a > function with Global => null only once), since it doesn't allow > anything that isn't already allowed for any Global => null function. > (At least, I've been assuming that there is a rule similar to the one > for functions in Pure packages that applies to such functions -- they > have similar purposes -- if not, we should fix AI12-0079-1, not this > AI!) > > Tucker had replied that "he was convinced", but the permission > remains. We actually discussed it a bit during the meeting, so I don't > want to delete it without getting the OK from this list. I am OK with your suggestion. Let me know if you would like some proposed wording for this Implementation Note (or Advice?). **************************************************************** From: Randy Brukardt Sent: Thursday, November 29, 2018 8:41 PM ... > > (9) I had suggested eliminating the Implementation Permission > > completely (replacing it with an Implementation Note suggesting the > > use of as-if optimizations and applying the permission to call a > > function with Global => null only once), since it doesn't allow > > anything that isn't already allowed for any Global => null function. > > (At least, I've been assuming that there is a rule similar to the > > one for functions in Pure packages that applies to such functions -- > > they have similar purposes -- if not, we should fix AI12-0079-1, not > > this AI!) > > > > Tucker had replied that "he was convinced", but the permission > > remains. We actually discussed it a bit during the meeting, so I > > don't want to delete it without getting the OK from this list. > > I am OK with your suggestion. Let me know if you would like some > proposed wording for this Implementation Note (or Advice?). I don't think Implementation Advice is needed here, as I have yet to meet an implementer who wants to generate worse code than they need to based on the language definition. ;-) Here is the Implementation Note that I put after the Bounded Error in the original AI (after deleting the permission): AARM Implementation Note: As always, an implementation may apply "as-if" optimizations (those that result in the same external effects in the absence of erroneous execution) to the function calls associated with user-defined literals. In particular, if the function associated with a _Literal aspect has a Global aspect that indicates no references to global variables, then a number of optimizations are available to the implementation: * The implementation can evaluate a user-defined literal function at compile-time if it has access to the body of the function (for example, if it is inlined), and that body doesn't reference anything evaluated at runtime. If the compile-time evaluation results in an exception, this bounded error allows the compilation unit to be rejected. * Implementations can use existing permissions (see 6.1.2) to avoid evaluating the function associated with a user-defined literal more than once for a particular literal value. This evaluation can be "hoisted" (done once per compilation unit during the elaboration of the unit) if the compiler can prove that the function doesn't depend on any constants or locals with a runtime value not yet elaborated. * If the literal value is not needed by the execution of the program, the function call can be omitted even if it might have side-effects (again, see 6.1.2). P.S. In case you're not up to speed on everything, "6.1.2" is the subclause added by AI12-0079-1. Tucker and I have been discussing off-line the permission that is currently missing from that AI. Much more on that when we discuss that AI again, most likely at our upcoming meeting. **************************************************************** From: Tucker Taft Sent: Sunday, December 2, 2018 10:59 AM Works for me! **************************************************************** From: Randy Brukardt Sent: Tuesday, February 19, 2019 6:44 PM Tucker Taft wrote a while back: [Editors Note: The thread this was from is filed in AI12-0208-1.] ... > The point of AI-249 is not to support odd-ball syntaxes for literals, > but rather to allow the *existing* syntax for numeric literals to be > used with private types. If the AI made you think that users were > being given the opportunity to invent some new kind of syntax for > literals, it definitely needs to be clarified. The first thing to > change is the !subject -- instead of "user-defined numeric literals" > it should be "numeric literals for user-defined types." And we should > use that terminology throughout, rather than ever saying "user-defined > literals." We really are not supporting that in this AI. I strongly disagree with this statement/proposal to change the terminology to "numeric literals for user-defined types". I was planning to ignore it, but since John is insisting on such a change, I need to make my reasons known. First of all, "user-defined types" is nonsense. It describes pretty much every type in an Ada program. And the handful of types it doesn't describe includes the motivating case for the feature -- the language-defined Big_Integer and Big_Real types. Second of all, the term "user-defined " has a long history of being used in an Ada context to mean a user-defined meaning for something. It has never meant allowing new lexical or syntactic items, just letting the user ascribe a meaning for existing lexical/syntax items. In particular, 4.1.6 is titled "User-Defined Indexing". This doesn't mean you get to use square brackets or some emojies to define indexing! It lets you give a user-defined meaning to the existing () notation. (And really, it is barely that.) 4.2.1 is (currently) titled "User-Defined Literals", there is only one subclause separating 4.1.6 and 4.2.1. It would look bizarre to use different terminology in 4.2.1 and in 4.1.6. Consistency alone would strongly suggest leaving the presentation as it is, as that is how we have presented such features in the past. --- I don't really understand John's confusion, nor why my 3 different private messages to him on the topic didn't succeed in dispelling that confusion. The primary purpose has always been to allow (existing) literals to be used with abstract data types; there didn't seem to be any reason to limit the feature *just* to ADTs (read, private types), since the feature has to make sense for the full type of any private type anyway. I agree that "user-defined" isn't the best terminology to use (here or anywhere), but I don't think inventing new terminology is necessarily going to help, as it would be more confusing to someone reading the future RM to have multiple presentations for what is essentially the same concept. I could see modifying the !problem and !proposal of the AI to emphasize ADTs and the fact that we are talking about existing lexical literals, but that doesn't require any changes to the !wording nor the !subject (and probably not the !summary, either. **************************************************************** From: Steve Baird Sent: Tuesday, February 19, 2019 6:58 PM > Second of all, the term "user-defined " has a long history of > being used in an Ada context to mean a user-defined meaning for > something. It has never meant allowing new lexical or syntactic items, > just letting the user ascribe a meaning for existing lexical/syntax items. > > In particular, 4.1.6 is titled "User-Defined Indexing". This doesn't > mean you get to use square brackets or some emojies to define > indexing! It lets you give a user-defined meaning to the existing () notation. This argument for calling the section "User-Defined Numeric Literals" makes sense to me. **************************************************************** From: Tucker Taft Sent: Tuesday, February 19, 2019 9:41 PM I agree that for consistency with other similar sections, "User-Defined Numeric Literals" might make sense as a (sub)clause title, but it also seems clear that John, and perhaps others, were confused by the terminology used in the AI. I think in the RM introduction to the section, and in the AI, we can emphasize that we are allowing (normal, everyday) numeric literals to be used with non-numeric, most likely private, types. **************************************************************** From: Jean-Pierre Rosen Sent: Wednesday, February 20, 2019 2:40 AM > ... >> The first thing to change is the >> !subject -- instead of "user-defined numeric literals" it should be >> "numeric literals for user-defined types." [...] > I strongly disagree with this statement/proposal to change the > terminology to "numeric literals for user-defined types". Here I agree with both sides: the literals themselves are not "user-defined", but talking about "user-defined types" in a language where all types obey the same rules makes me uncomfortable. So I propose "user defined interpretation of numeric literals". It is a bit longer, but it conveys the right idea. Of course, someone may suggest a better word than "interpretation": meaning, transformation, magic... ****************************************************************