AI22-0002-1

!standard 13.1.1(18.3/5)          21-11-18 AI22-0002-1/03

!standard 4.3.5(6/5)

!standard 4.3.5(10/5)

!standard 4.3.5(35/5)

!class binding interpretation 21-11-11

!status Amendment 1-2022 22-01-14

!status WG9 Approved 22-10-18

!status ARG Approved 12-0-0 21-11-18

!status work item 21-11-11

!status received 21-08-02

!submitter Randy Brukardt

!priority Low

!difficulty Medium

!qualifier Omission

!subject Nonoverridable aspects must be primitive

!summary

Nonoverridable aspects (or elements thereof) that denote subprograms with a parameter or result of the associated type T or access T must denote primitive subprograms.

!issue

Is the following legal? (No.)

package Pkg is
   package Pkg_For_Root is
      type Root is tagged record
         X, Y : Integer;
      end record with
         Integer_Literal => I_L;
      package Not_Primitive  is
         function I_L (S : String) return Root is ((0, 0));
      end Not_Primitive;
      use Not_Primitive;
   end Pkg_For_Root;
   package Pkg_For_Derived is
      type Descendant is new Root with record Z : Integer; end record;
   end Pkg_For_Derived;
end Pkg;

If it is not legal, which type is illegal? (Root.)

!recommendation

(See Summary.)

!wording

Add after 13.1.1(18.3/5):

For a nonoverridable aspect of a type T that denotes one or more subprograms with a parameter or result of type T or access T, all of the denoted subprograms shall be primitive for T. The same restriction applies to elements that denote subprograms within a nonoverridable aspect that is in the form of an aggregate.

AARM Reason: Nonoverridable aspects can only be confirmed on inheritance, and a name that denotes a nonprimitive subprogram continues to denote the original subprogram, thus there would be an inherited reference to a subprogram that operates on the parent type rather than the derived type. An example of an aspect that has elements that denote subprograms is the Aggregate aspect.

---

Clean-ups:

Add after AARM 4.1.6(3.b/3):

AARM Reason: For a function that has a parameter of T or access T, a blanket rule in 13.1.1 requires the function to be a primitive operation of T, the wording about the same declaration_list is redundant in that case. That 13.1.1 rule does not apply to functions that have parameters of T'Class or access T'Class (which is good, as those can never be primitive); the wording about the declaration_list is necessary in that case.

Add after AARM 5.5.1(8.a/3):

AARM Reason: For a function that has a parameter of T or access T, a blanket rule in 13.1.1 requires the function to be a primitive operation of T, the wording about the same declaration_list is redundant in that case. That 13.1.1 rule does not apply to functions that have parameters of T'Class or access T'Class (which is good, as those can never be primitive); the wording about the declaration_list is necessary in that case.

Modify 4.3.5(6/5):

The name specified for Empty for an Aggregate aspect shall denote [a constant of the container type, or denote] exactly one function with a result type of the container type that has no parameters, or that has one in parameter of a signed integer type.

Modify AARM 4.3.5(6.a/5):

[In the function case, t]{T}he parameter{ of the function}, if present may be used to ...

Modify 4.3.5(10/5):

If the container type of an Aggregate aspect is a private type, the full type of the container type shall not be an array type.[ If the container type is limited, the name specified for Empty shall denote a function rather than a constant object.]

Modify 4.3.5(35/5):

!discussion

We start by noting that no confirming aspect is possible in this case. 13.1.1(18.4/5) requires that the aspect denote the same declaration, but that declaration necessarily has the wrong profile (as the result type is wrong). A similar declaration, as in the following:

package Pkg2 is
   package Pkg_For_Root is -- same as in !question
      type Root is tagged record
         X, Y : Integer;
      end record with
         Integer_Literal => I_L;
      package Not_Primitive  is
         function I_L (S : String) return Root is ((0, 0));
      end Not_Primitive;
      use Not_Primitive;
    end Pkg_For_Root;
    package Pkg_For_Derived is
       type Descendant is new Root with record Z : Integer; end record
          with Integer_Literal => I_L;

       package Not_Primitive is
          function I_L (S : String) return Descendant is ((0, 0, 0));

       end Not_Primitive;
       use Not_Primitive;
    end Pkg_For_Derived;
end Pkg2;

Clearly fails 13.1.1(18.4/5), as it denotes an entirely different declaration which was not inherited.

But this means that the type Descendant in the question has an Integer_Literal aspect that returns the wrong type.

Originally, the sentence in 13.1.1(18.3/5) "Any legality rule associated with a nonoverridable aspect is re-checked for the derived type, if the derived type is not abstract" was interpreted as making Descendant illegal in this case. However, the profile of an aspect is a Name Resolution Rule, not a Legality Rule, so clearly that rule does not help.

Moreover, we do not want to re-resolve the original name, as one can get an altogether different answer (as in the second example).

So we need a new, explicit, rule to cover this case.

The obvious rule would be to say that a derived type is illegal if it has any inherited nonoverridable aspect (or part thereof) that is not a primitive subprogram (as such subprograms are not inherited). However, a rule like this has to be rechecked in generic units (as it applies even though no aspect is directly specified on the type, and we can only know if the aspect is specified on the actual type). That adds implementation complexity.

Moreover, such a rule would provide a back-door way to declare a "final" type (one that can never be derived from). That seems like a bad idea; if there is a need for "final" types, they should be provided explicitly, not as a side-effect of other rules.

The alternative is to simply require the subprograms to be primitive in the first place. Such a rule would appear to have some negative side effects.

First, such a rule would ban using subprograms imported from other packages. However, this is not a real problem, as all of these operations necessarily have a parameter or result of the type. Those obviously cannot be declared before the type in question, and since the names are resolved at the first freezing point, they clearly cannot be declared afterwards (in a child unit), either. So they have to be declared in this unit. Unless someone is intentionally trying to break up the name space and make things nonprimitive, this could only happen if the subprogram is declared in an instance that appears after the type declaration (and has a parameter of the type). In that case, the subprogram can be renamed, which would have the effect of making it primitive. So there is an easy workaround even in this unlikely case.

Second, a non-derived type declared in a declarative_part (as in a subprogram) has no primitive operations, so such a rule would ban using a nonoverridable aspect on such a type. However, there is a simple mitigation for this problem (declare the type in a package nested in the subprogram's declarative_part). Moreover, these aspects are generally complex aspects used for adding functionality to an abstract data type; such types will almost always be declared in packages and thus there will be no problem in actual use.

Therefore, we adopt a rule that most subprograms denoted in nonoverridable aspects must be primitive subprograms.

[Aside: If we ever did support "final" types, we could could make the new rule conditional on the type not being final.]

-----

One might think that we could remove some restrictions from existing aspects which have a similar effect to the new blanket rule. However, all of the aspects with "same declaration list" requirements also allow class-wide subprograms. Those are not covered by the new blanket rule, so we need to leave those restrictions. We added AARM notes to make it clear that there is a stronger blanket requirement for routines that are not class-wide.

We also remove the option to use a constant as the initial value for the Aggregate aspect. A constant acts very much like a non-primitive function for the type, and we just decided we don't want those. Note that one can easily write an expression function that returns a constant if one really wants to use a constant for initialization (some of the Ada.Containers do that already) - no body is required.

-----

Note that the proposed rule is mildly incompatible. If someone has used one of the Ada 2012 nonoverridable aspects (Constant_Indexing, Variable_Indexing, Default_Iterator) to which this rule applies with a non-primitive subprogram (including the case where the type is declared in a declarative_part of a subprogram or other entity), and did not derive from them, the aspect would have worked as expected but now would be illegal. However, all of these aspects already have a "same declaration list" rule, so in package specifications the effect is the same. And as described previously, it is thought that use of these aspects for types declared in subprograms or blocks will be unlikely.

Also note that this rule does not apply to Default_Iterator/Constant_Indexing/ Variable_Indexing if the subprogram that is resolved has a parameter of T'Class or access to T'Class (the parameter or result type is not T in that case). It also does not apply to Iterator_Element (which names a type), Implicit_Dereference (which names a discriminant), No_Controlled_Parts (which is a Boolean value), or Max_Entry_Queue_Length (which is an integer value).

There is also an incompatibility with the original Ada 202x, in that a constant is no longer allowed for Empty. As a brand-new feature, uses in existing code have to be rare.

!corrigendum 4.3.5(6/5)

@drepl
The @fa{name} specified for Empty for an Aggregate aspect shall denote a
constant of the container type, or denote exactly one function with a result
type of the container type that has no parameters,
or that has one @b{in} parameter of a signed integer type.
@dby
The @fa{name} specified for Empty for an Aggregate aspect shall denote
exactly one function with a result type of the container type that has no
parameters, or that has one @b{in} parameter of a signed integer type.

!corrigendum 4.3.5(10/5)

@drepl
If the container type of an Aggregate aspect is a private type, the full
type of the container type shall not be an array type. If the container type is
limited, the name specified for Empty shall denote a function rather than a
constant object.
@dby
If the container type of an Aggregate aspect is a private type, the full
type of the container type shall not be an array type.

!corrigendum 4.3.5(35/5)

@drepl
@xbullet{if the @fa{aggregate} is not an indexed aggregate, by assignment
from the Empty constant, or from a call on the Empty function specified in
the Aggregate aspect. In the case of an Empty function with a formal
parameter, the actual parameter has the following value:}
@dby
@xbullet{if the @fa{aggregate} is not an indexed aggregate, from a call
on the Empty function specified in
the Aggregate aspect. In the case of an Empty function with a formal
parameter, the actual parameter has the following value:}

!corrigendum 13.1.1(18.3/5)

@dinsa
Certain type-related aspects are defined to be @i{nonoverridable}; all
such aspects are inherited by derived types according to the rules
given in 13.1. Any legality rule associated with a nonoverridable
aspect is re-checked for the derived type, if the derived type is not
abstract. Certain type-related and subtype-specific aspects are
defined to be @i{additive}; such aspects are not inherited, but they can
@i{apply} to the types derived from, or the subtypes based on, the
original type or subtype, as defined for each such aspect. Finally,
certain type-related aspects are @i{implicitly composed}; such aspects
are not inherited, but rather a default implementation for a derived
type is provided, as defined for each such aspect, based on that of
its parent type, presuming the aspect for the parent type is available
where the derived type is declared, plus those of any new components
added as part of a type extension.
@dinst
For a nonoverridable aspect of a type @i{T} that
denotes one or more subprograms with a parameter or result of type @i{T} or
@b{access} @i{T}, all of the denoted subprograms shall be primitive for @i{T}.
The same restriction applies to elements that denote subprograms within a
nonoverridable aspect that is in the form of an @fa{aggregate}.

!ACATS test

!appendix

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