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

!summary

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.

!issue

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  
    with Default_Value => Char (Ada.Characters.Latin_1.Nul);

 

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.)

!recommendation

(See Summary.)

!wording

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.

!discussion

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;
  private
     subtype S is T;
     type T is range 1 .. S'Last;
        -- illegal, but not because of the current instance rule

 

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.

!example

This AI would allow the following declarations to be legal:

Nul_Pos : constant Integer :=
   Character'Pos (Ada.Characters.Latin_1.NUL);   --  equals 0
 
type Char is new Character
   with Default_Value => Char'Val (Nul_Pos);

-- Or alternatively...

type Char2 is new Character
     with Default_Value => Char'First;

!ACATS test

An ACATS C-Test is needed to check that these attributes are allowed for a Default_Value aspect specification.

!appendix

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.

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