AI22-0036-1
!standard 3.5(56.3/3) 22-04-26 AI22-0036-1/02
!class binding interpretation 22-01-26
!status work item 22-01-26
!status received 21-04-28
!priority Low
!difficulty Easy
!qualifier Omission
!subject Attributes in the expression of Default_Value
Attributes 'First, 'Last, and 'Val are specially recognized in the expression
of a Default_Value aspect.
If one wants to create a Character type for which the default value is the NUL
character, (Ada.Characters.Latin_1.Nul), there does not appear to be any legal way to do it. One needs some sort of conversion of the Nul value (since it is not an enumeration literal
of the type, so it is not inherited), but all such conversions are illegal.
For instance:
type Char is new Character |
This is illegal because Char is a constant object (the current instance of
type Char), and thus cannot be used as the prefix of a type conversion.
Should some way be available? (Yes.)
(See Summary.)
Add after 3.5 (56.g/5)
For the current instance of a type T used as the aspect_definition of a specification for aspect Default_Value, the following attributes are defined:
T'First T'First denotes the lower bound of the range of T. The value of this attribute is of the type of T.
T'Last T'Last denotes the upper bound of the range of T. The value of this attribute is of the type of T.
Modify 13.14(10/5)
At the place where an expression causes freezing, the type of the expression is frozen, unless the expression is {either} an enumeration literal used as a discrete_choice of the array_aggregate of an enumeration_representation_clause or as the aspect_definition of a specification for aspect Default_Value{, or an attribute reference with attribute designator First, Last, or Val used as the aspect_definition of a specification for aspect Default_Value}.
Modify 13.14(10.a.1/5)
We exclude uses of enumeration literals {and specific attribute references with static values} that are used in part for setting the representation of the type (aspect Default_Value is defined to be a representation aspect) so we don't freeze the type while determining its representation.
We previously decided in AI12-0367-1 to only add a narrow rule allowing
enumeration literals. We considered other cases at that time, and did not
think they were important enough. We did not consider this example at that
time, and we probably would have had a different opinion if we had done so.
The best solution to this problem is to allow at least T'Val, as it allows
arbitrary position numbers of the type. The use of T'First and T'Last also
seem natural (we know the values without freezing, as the bounds of any
scalar *type* are required to be static).
However, inside of an aspect specification, the name of the type refers to
the current instance of the type. The usual scalar attributes are not
defined on an object, and even if they were, referencing the current
instance would cause circular freezing.
In order to avoid both of these problems, we need to specially define the
use of these attributes in the expression of a Default_Value aspect
specification.
Note that we don't want to allow any scalar attribute. Many attributes would
necessarily require the type to be frozen first (think 'Size or 'Enum_Rep),
which would cause circularities. Similarly, We only define these for the
associated type; allowing them on a subtype of the type could cause circular
freezing issues for the same reason (the bounds of the subtype could involve
an attribute that itself would require freezing of the base type, which is
circular).
[Editor's opinion: This seems to me to be quite a bit of mechanism for a
problem that only occurs for the type Character, Wide_Character, and
Wide_Wide_Character (as these are the only enumeration types that do not have
a nameable enumeration literals for every position). As such, it seems to
make more sense as an Amendment; it can then be implemented if there is
sufficient demand for it (it will be interesting to see if others run into
this problem).]
We considered whether ‘Enum_Val should also be allowed. While that is a
possibility, it was felt that the attributes ‘First, ‘Last, and ‘Val provide enough
capability to deal with a need that should be quite rare in practice, and furthermore,
‘Val is more broadly useful than ‘Enum_Val, since it can be applied to other scalar
types than enumeration types.
This AI would allow the following declarations to be legal:
Nul_Pos : constant Integer := |
An ACATS C-Test is needed to check that these attributes are allowed for a
Default_Value aspect specification.
From: Brad Moore [privately]
Sent: Saturday, December 4, 2021 11:01 PM
I stumbled onto a problem for which I thought would be easy to do in Ada, but
appears to be very difficult if not, impossible.
I wanted to create a Character type for which the default value is the NUL
character, (Ada.Characters.Latin_1.Nul)
The closest I got was;
type Char is new Character
with Default_Value => Char (Ada.Characters.Latin_1.Nul);
But that gives me a compile error in GNAT.
error: aspect specification causes premature freezing of "Char",
which makes sense to me.
If it was a graphic character, such as space, then this would be easy.
type Char is new Character with Default_Value => ' ';
But since Nul isn't a graphic character, I cant specify it in a literal.
I even tried using the Integer_Literal aspect, to get the value 0 to to be
used, using a fairly complex expression using 'Val and 'Pos, but that also
gave me a compile error stating that the value 0 (for Default_Value), had
to be a static expression, probably because of all the machinations to go
from 0 to a Char value.
Do you agree this should be an AI? And if so, should an AI be created, or
should we just not bring this up right now, while we are standardizing Ada
2022, and remember to bring it up farther down the road, when we are ready
to look at Ada 2022 issues?
****************************************************************
From: Randy Brukardt [privately]
Sent: Sunday, December 5, 2021 6:20 PM
When we discussed this originally (in AI12-0367-1), we considered various
rules. But ultimately a narrow exeception solely for enumeration literals
in a Default_Value was proposed (as there already was a similar exception
for enumeration representation clauses). We knew there were things that
would be hard to do (they're called out in the !discussion), but I'm not
sure that anyone thought of this particular case (which is only a problem
because some of the positions in Character do not have nameable enumeration
literals).
I could see allowing attributes of the type (which would allow 'Val and
'First, either of which could solve this problem), but that has semantic
issues (you would be allowing static expressions that you can't safely
evaluate as the type isn't frozen). A compiler would probably need some
special case code to deal with such cases, which is ugly when only a handful
of types are involved.
It may make more sense to leave this impossible as the fix is worse than the
problem. (Note that one can define a string type with a default, the problem
only occurs for Character, Wide_Character, and Wide_Wide_Character.)
I suppose we need an AI to discuss it, though.
****************************************************************
From: Tucker Taft [privately]
Sent: Sunday, December 5, 2021 9:45 PM
I could see allowing the use of 'Val, 'Pos, 'First, and 'Last in the
Default_Value aspect. These would be quite natural to use in such an aspect,
even for a numeric type. We are trying to identify a particular value, which
really has nothing to do with its representation.
****************************************************************
From: Randy Brukardt [privately]
Sent: Monday, December 6, 2021 9:58 PM
We'd have to be careful with 'First and 'Last, as they could be any arbitrary
static expression (especially if a subtype is involved). 'Enum_Rep and
'Enum_Val could be a problem.
A static expression freezes in all contexts other than an aspect_spec by
13.14(8/4). So that means that you can't allow using 'First or 'Last in
Default_Value unless it is of the type itself and there is no constraint for
the type. (Anything else would have frozen the type before the aspect specs
were defined - that would be the circular freezing that the freezing rule is
intended to prevent.) Seems like an unnecessary complication, since the value
involved is directly in the associated declaration.
I'd probably limit any additional rule to T'Val in the definition of T, simply
to avoid any extra complications.
I'm thinking of a case like:
type T is (A, B, C, D, E) with Default_Value => S'First;
subtype S is T range T'Enum_Val(1) .. T'Enum_Val(4); -- (1)
The subtype freezes T at (1), but that needs the value of S'First, which needs
the frozen rep. values of T. The attributes at (1) meet the requirements of
4.9(7) to be static (if they didn't, T'Val(1) would not be static, which would
be nonsense). One could have an enumeration rep. clause for T to make this more
interesting, for instance:
for T use (A => 1, B => 2, C => 4, D => 8, E => 16);
The original freezing rule exists to prevent this sort of circularity
(possible because resolution isn't done until the freezing point for aspect
specs).
****************************************************************
From: Tucker Taft [privately]
Sent: Tuesday, December 7, 2021 8:11 AM
I presumed we were only talking about special handling for 'First, 'Last,
'Val, and 'Pos where the prefix is the current instance of a scalar type.
With any other prefix, they would be treated like normal uses of the
attribute. Normally a prefix that is a current instance would be interpreted
as a value/object, so there needs to be some special handling in any case, so
allowing the use of these attributes, which aren't even defined for scalar
objects, without freezing seems reasonable.
****************************************************************
From: Stephen Baird [privately]
Sent: Tuesday, December 7, 2021 9:02 AM
This probably isn't relevant, but I'll note that Brad's original attempt
> type Char is new Character with Default_Value => Char
> (Ada.Characters.Latin_1.Nul)
also runs afoul of the current instance rule (8.6(17)).
****************************************************************
From: Tucker Taft [privately]
Sent: Tuesday, December 7, 2021 9:17 AM
I would say it is highly relevant. Almost any attempt to define a
Default_Value "programmatically" is going to run afoul of the current
instance rule. 'First or 'Last seem by far the most likely to be used
in such an attempt, with 'Val(NN) to be the next most likely.
****************************************************************
From: Brad Moore [privately]
Sent: Tuesday, December 7, 2021 9:29 PM
Yes, I was assuming I'd need to do a cast to get to the new type, so not sure
I would have ordinarily tried to use 'First or 'Var without doing the cast.
But I think if the compiler could give a helpful suggestion to use an allowed
attribute, that would have worked, if we decided to allow this.
****************************************************************
From: Randy Brukardt [privately]
Sent: Wednesday, December 8, 2021 12:29 AM
One can work around the current instance issue with a subtype, as I showed
yesterday, and indeed all of the examples of the need for the freezing rule
in the first place involved such subtypes. But I suppose for practical
reasons, one would usually use the type itself. Of course, this makes this
a fairly complex suggestion -- one has special case rules for a handful of
attributes in a single context not just for freezing but even for the
interpretation of the meaning. I have to wonder if such a change is worth it,
given that there is only a true problem for derivatives of types Character,
Wide_Character, and Wide_Wide_Character - something I'd discourage anyway.
****************************************************************
From: Tucker Taft [privately]
Sent: Wednesday, December 8, 2021 9:22 AM
I guess I would claim that an aspect spec such as "Default_Value => T'First"
might be quite common even for a numeric type, and forcing the user to
explicitly use the low bound for the type instead would actually obfuscate
the intent. So I don't see this as a corner case. I would agree T'Val(0) is
more of a corner case, but if we allow T'First then allowing T'Val seems easy
enough. Having to declare a subtype for this purpose is truly ugly.
****************************************************************