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

!summary

Attributes 'First, 'Last, and 'Val  are specially recognized in the expression

of a Default_Value aspect.

!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

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.

!discussion

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.

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

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