Version 1.4 of ai12s/ai12-0249-1.txt

Unformatted version of ai12s/ai12-0249-1.txt version 1.4
Other versions for file ai12s/ai12-0249-1.txt

!standard 4.2(9)          18-11-29 AI12-0249-1/03
!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 literals
!summary
A mechanism for user-defined literals is added.
!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 a type to use numeric literals.
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. The evaluation of the literal null yields the null value of the expected type. 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).
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 (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.
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 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.
* 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)
Replace the paragraph:
The evaluation of a numeric literal, or the literal null, yields the represented value.
by:
If its expected type is a numeric type, the evaluation of a numeric literal yields the represented value. The evaluation of the literal null yields the null value of the expected type. 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).
!corrigendum 4.2.1(0)
Insert new clause:
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 (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.
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.
!corrigendum 4.9(3)
Replace the paragraph:
by:
!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 <blah> literals is added." 
(where <blah> 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.

****************************************************************

Questions? Ask the ACAA Technical Agent