AI22-0036-1
!standard 8.6(17/3) 22-12-29 AI22-0036-1/06
!standard 13.14(4/1)
!standard 13.14(7.2/5)
!standard 13.14(8/4)
!standard 13.14(8.2/1)
!standard 13.14(10/5)
!standard 13.14(15.2/5)
!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
The attributes First, Last, Val, and Pos can be used in the expression of a Default_Value aspect specification, as can an enumeration literal. This requires changes to the freezing rules, which we reorganize to clarify the distinction between what constructs "cause freezing" and what entities are "frozen" when used in a construct that causes freezing.
The “current instance” rule does not apply when the name of a type is used as the prefix of an attribute within the expression of a Default_Value aspect specification and the prefix of the attribute is expected to be a type or subtype, not an object.
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.)
Replace the final sentence of 8.6(17/3):
These rules do not apply if the usage name appears within the subtype_mark of an access_definition for an access-to-object type, or within the subtype of a parameter or result of an access-to-subprogram type.
with a bulleted list:
These rules do not apply if the usage name appears:
AARM Discussion: Language-defined scalar subtype attributes that might be used here include First, Last, Val, and Pos. Enum_Val and Enum_Rep could not be used as they would freeze the type.
Modify and reorder 13.14, starting at (4/1) and continuing through (15.3/5)):
A construct that (explicitly or implicitly) references an entity can cause the freezing of the entity, as defined by subsequent paragraphs. At the place where a construct causes freezing, each name, expression, implicit_dereference, or range within the construct causes freezing{, except as defined below}:
An implicit call freezes the same entities and profiles that would be frozen by an explicit call. This is true even if the implicit call is removed via implementation permissions.
The following rules define which entities are frozen at the place where a construct causes freezing:
Notwithstanding the rest of this subclause, freezing an incomplete view has no effect.
It might seem at first that the current instance change would open the door for freezing
problems that were previously prevented via the current instance rule. This is typically not the case because of the possibility of bypassing the current instance rule by means of a partial view and a subtype, as in
type T is private; |
Most constructs one might attempt which become illegal as a result of the current instance rule can be reformulated to use the name of a subtype instead of a name that denotes the current instance. In such cases, changing the current instance rule to apply in fewer cases would not introduce any substantially new freezing scenarios.
However, since it is illegal for an aspect specification to contain something that would freeze the entity itself (directly or indirectly), we also need to create an additional “hole” in the freezing rules in order to achieve the intended effect. We noted that the position numbers of a type are determined solely by the type’s definition: the literals and their order for an enumeration definition, and static expressions for an integer definition. Thus it is safe to allow Pos and Val in such contexts. Similarly, the values for First and Last are determined by the type’s definition.
We make the “hole” as small as possible, so we only allow those attributes (and the equivalent Range) and only in the expression of Default_Value. Having attributes that do not freeze could cause issues (either semantically or of implementation) in other contexts, and there is no obvious additional expressivity in allowing them.
In general we considered two ways of creating "holes" in the freezing rules. One is to say that certain entities are not frozen even though they are used in a construct that causes freezing. The alternative is to say that certain constructs do not cause freezing, so it doesn't matter what entities they use, since no freezing is caused by the construct. After trying both ways, we settled on specifying that certain (parts of) constructs do not cause freezing. This was simpler because constructs like S'Pos(...) can reference multiple entities, and we didn't want to have to add special cases for each such entity. Instead, we simply said that when such expressions appear as part of a static expression within a certain context, they do not cause freezing.
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.
****************************************************************