Version 1.3 of ai22s/ai22-0012-1.txt

Unformatted version of ai22s/ai22-0012-1.txt version 1.3
Other versions for file ai22s/ai22-0012-1.txt

!standard 6.1.1(18.2/5)          21-11-18 AI22-0012-1/02
!class binding interpretation 21-11-12
!status ARG Approved 10-0-2 21-11-18!status work item 21-11-12
!status received 21-07-07
!priority Medium
!difficulty Hard
!qualifier Error
!subject Incompatibility for abstract type Pre'Class
!summary
The wording in 6.1.1(18.2/5) is adjusted to disallow only expressions containing nondispatching invocations of abstract functions, rather than requiring the expression to be static.
!question
Suppose we declare an abstract type T0 with a nonabstract primitive Foo, with a nonabstract Pre'Class invoking All_Good:
package P is type T0 is abstract tagged private; procedure Foo(X : T0) with Pre'Class => All_Good(X);
function All_Good(X : T0) return Boolean; private ... end P;
We then derive another type T1 from T0, and override the Foo primitive to call the primitive of its parent type, and then do something more:
with P; package P1 is type T1 is new T0 with private;
procedure Foo(X : T1); private ... end P1;
package body P1 is procedure Foo(X : T1) is begin P.Foo(T0(X)); ... end Foo; end P1;
This was legal in Ada 2012, but would be illegal in Ada 2022 because of 6.1.1(18.2/5) which comes from AI12-0412-1.
Should this be legal in Ada 2022? (Yes.)
!recommendation
RM 6.1.1(18.2/5) currently says:
If the primitive subprogram S is not abstract, but the given descendant of T is abstract, then a nondispatching call on S is illegal if any Pre'Class or Post'Class aspect that applies to S is other than a static boolean expression. Similarly, a primitive subprogram of an abstract type T, to which a non-static Pre'Class or Post'Class aspect applies, shall neither be the prefix of an Access attribute_reference, nor shall it be a generic actual subprogram for a formal subprogram declared by a formal_concrete_subprogram_declaration.
Note the use of "static" rather than "nonabstract" in the above paragraph. Originally this was thought to be a corner case, so an easier rule was proposed. But there are users who have already bumped into this incompatibility using an early implementation of Ada 2022.
The wording should be fixed to avoid this incompatibility.
!wording
Modify 6.1.1(18.2/5) as follows:
If the primitive subprogram S is not abstract, but the given descendant of T is abstract, then a nondispatching call on S is illegal if {the corresponding expression for} any Pre'Class or Post'Class aspect that applies to S [is other than a static boolean expression]{includes a nondispatching invocation of an abstract function}. Similarly, a primitive subprogram of an abstract type T, to which [a non-static]{such a} Pre'Class or Post'Class aspect applies, shall neither be the prefix of an Access attribute_reference, nor shall it be a generic actual subprogram for a formal subprogram declared by a formal_concrete_subprogram_declaration.
Delete AARM 6.1.1(18.f/5).
!discussion
This incompatibility was introduced by AI12-0412-1. The goal was to allow an abstract class-wide pre/post on a non-abstract function, presuming that the abstract pre/post functions would be overridden in a descendant. Unfortunately, there is an incompatibility for a case where there is an abstract type with a nonabstract primitive, with nonabstract class-wide pre/post. AI12-0412-1 made it illegal to call such a subprogram using a non-dispatching call, if the class-wide pre/post was nonstatic. In Ada 2012, calling such a subprogram was legal.
Note that this situation can occur both when the Pre'Class or Post'Class is first resolved, and time that an abstract routine is bound (even if the resolved routine is not abstract). Remember that this is talking about the nominal NT, but ultimately one can bind to a derived type with an abstract version of one of the included functions.
Note that the following was added into the AARM for Ada 2022, after 6.1.1(18.2/5) (as AARM 6.1.1(18.f/5)):
Discussion: {AI12-0412-1} As this Reference Manual was frozen, a significant incompatibility has come to light with the above rule. The wording makes calls to non-abstract primitives of a tagged abstract type illegal even if no abstract routines are involved in the Pre'Class or Post'Class. It is likely that the above rule will be adjusted; check with ARG work at www.ada_auth.org/arg.html to find the adjusted rules.
This note is deleted by this AI, as we've fixed the rule.
!ACATS test
ACATS B-Tests should be adjusted to not include cases allowed by this change. There might be value to having an ACATS C-Test to ensure that an example like the one in the question is legal and executes properly.
!appendix

From: Tucker Taft
Sent: Wednesday, July 7, 2021  12:45 PM

I believe we missed an incompatibility with AI12-0412-1.  Our goal was to 
allow an abstract class-wide pre/post on a non-abstract function, presuming
that the abstract pre/post functions would be overridden in a descendant.  
Unfortunately, I believe we might have created an incompatibility for a case
where we have an abstract type with a nonabstract primitive, with
*nonabstract* class-wide pre/post.  AI12-0412-1 made it illegal to call such
a subprogram using a non-dispatching call, if the class-wide pre/post was
*nonstatic*.  In Ada 2012, I believe calling such a subprogram was legal.

For example, if we declare an abstract type T0 with a nonabstract primitive 
Foo, with a nonabstract Pre'Class invoking All_Good:

   package P is
        type T0 is abstract tagged private;
        procedure Foo(X : T0) with Pre'Class => All_Good(X);

        function All_Good(X : T0) return Boolean;
   private
       ...
   end P;

We then derive another type T1 from T0, and override the Foo primitive to call
the primitive of its parent type, and then do something more:

   with P;
   package P1 is
         type T1 is new T0 with private;

         procedure Foo(X : T1);
   private
        ...
   end P1;

   package body P1 is
         procedure Foo(X : T1) is
         begin
              P.Foo(T0(X));
              ...
         end Foo;
   end P1;

As far as I can tell, this was legal in Ada 2012, but would be illegal in Ada 
2022 because of AI12-0412-1, which says, in RM 6.1.1(18.2/5):

  If the primitive subprogram S is not abstract, but the given descendant of T 
  is abstract, then a nondispatching call on S is illegal if any Pre'Class or 
  Post'Class aspect that applies to S is other than a static boolean
  expression. Similarly, a primitive subprogram of an abstract type T, to
  which a non-static Pre'Class or Post'Class aspect applies, shall neither
  be the prefix of an Access attribute_reference, nor shall it be a generic
  actual subprogram for a formal subprogram declared by a
  formal_concrete_subprogram_declaration.

Note the use of "static" rather than "nonabstract" in the above paragraph.  I 
think we thought this was a corner case, so we went with the easier rule.  But
we have a customer who already bumped into this apparent incompatibility.

I think the wording should probably be modified as follows:

  If the primitive subprogram S is not abstract, but the given descendant of T
  is abstract, then a nondispatching call on S is illegal if {the
  corresponding expression for} any Pre'Class or Post'Class aspect that applies
  to S [is other than a static boolean expression]{would resolve to include an
  invocation of an abstract function}. Similarly, a primitive subprogram of an
  abstract type T, to which [a non-static]{such a} Pre'Class or Post'Class
  aspect applies, shall neither be the prefix of an Access attribute_reference,
  nor shall it be a generic actual subprogram for a formal subprogram declared
  by a formal_concrete_subprogram_declaration.

Groan...

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

Questions? Ask the ACAA Technical Agent