AI22-0121-1

!standard 9.5(7.2/5)                                   25-04-24  AI22-0121-1/04

!standard D.1(8.2/3)

!standard D.2.6(9.6/5)

!standard D.16(10/5)

!class binding interpretation 24-10-24

!status Amendment 1-2022  25-01-10

!status WG9 Approved 25-07-18

!status ARG Approved  15-0-0  25-01-09

!status work item 24-10-24

!status received 24-08-12

!submitter Stephen Baird

!priority Low

!difficulty Easy

!qualifier Omission

!subject Internal calls in contracts of protected types

!summary

Internal calls are not allowed in Dynamic_Predicate or Default_Initial_Value aspects of protected types.

!issue

Given:

   protected type T1 with Dynamic_Predicate => Get_X /= 123; -- ???
   is

      function Get_X return Integer;
   private
      X : Integer := 0;
      function Get_X return Integer is (X);
   end T1;

   Obj : T1;

   Flag : Boolean := Obj in T1;

 

The evaluation of the membership test involves a call to the protected operation Get_X.

This call of Get_X has the form of an internal call, yet no protected action is occurring when it is made. How can this be meaningful? (It isn’t.)

!recommendation

(See Summary.)

!wording

Modify 9.5(7.2/5):

An internal call on a protected function shall not occur within{:}

 Add after D.1(8.2/3):

An internal call shall not occur within the expression of a Priority or Interrupt_Priority aspect of a protected type.

AARM Reason: Such a call cannot be meaningful, since it would be evaluated during the creation of the object (before it is elaborated).

 Add after D.2.6(9.6/5):

An internal call shall not occur within the expression of a Relative_Deadline aspect of a protected type.

AARM Reason: Such a call cannot be meaningful, since it would be evaluated during the creation of the object (before it is elaborated).

Add after D.16(10/5):

An internal call shall not occur within the expression of a CPU aspect of a protected type.

AARM Reason: Such a call cannot be meaningful, since it would be evaluated during the creation of the object (before it is elaborated).

!discussion

9.5(7.2/5) makes an internal call illegal for a precondition, as no protected action is occurring when the precondition is evaluated. (A postcondition is different, it does occur in a protected action, and thus it can contain internal calls.) Since we already know this is a problem in some cases, it is not surprising that there are more such cases.

It appears that Dynamic_Predicates should have a rule similar to 9.5(7.2/5). Moreover, any aspect allowed on a protected type that has the value of an expression needs such a rule. For instance, this can happen within a Default_Initial_Condition. It might appear that Type_Invariant can cause a similar problem, but those cannot be given directly on a protected type (they are allowed only on a private type), and thus internal calls are never possible.

Similarly, internal calls should be banned from the Priority and Interrupt_Priority aspects. This is not a useful construct as the expression is evaluated while the object is being created (and thus before it is elaborated). It wouldn’t make sense anyway because it would not be in a protected action, either. We put that rule into D.1 as these aspects are defined in a Specialized Needs Annex and thus shouldn’t be mentioned in the core.

Similar logic applies to the Relative_Deadline and CPU aspects.

[Editor’s note: There does not seem to be a rule like D.16(11/5) which specifies when the expression of the Relative_Deadline aspect is evaluated. That seems to be missing, so the above statement is somewhat shaky. I think that is a separate issue to be dealt with in a separate AI. It would seem to me that the model was intended to be the same as the other aspects.]

External calls are still allowed for this usage. An external call might cause a race condition in some uses (because the check is not indivisible with later operations on the type), so that is not a particularly useful construct, but it at least makes sense.

Writing an external call for this example requires using a wrapper, because just adding a prefix does not change a call inside the type declaration into an external call (T1.Get_X is also an internal call as T1.Get_X is an expanded name). One needs a helper function to make this external call:

   protected type T1 with Dynamic_Predicate => T1.Obj_Get_X /= 123;

   is
       function Get_X return Integer;
   private
      X : Integer := 0;
      function Get_X return Integer is (X);
   end T1;
   function Obj_Get_X (Obj : in T1) return Integer is (Obj.Get_X);

We don’t consider this situation to be very likely. Most Dynamic_Predicates are written on separate subtypes, not the first subtype, and any protected call in a subtype declaration is necessarily an external call. Most users will not encounter this new rule (which only applies to first subtypes).

The following is the more likely way to write this predicate[a]:

   protected type T2 is
       function Get_X return Integer;
   private
      X : Integer := 0;
      function Get_X return Integer is (X);
   end T2;
   subtype Bounded_T2 is T2
      with Dynamic_Predicate => Bounded_T2.Get_X /= 123; -- OK.

!example

(See Issue and Discussion.)

!corrigendum 9.5(7.2/5)

@drepl

An internal call on a protected function shall not occur within a precondition expression (see @ref{6.1.1}) of a protected operation nor within a @fa{default_expression} of a @fa{parameter_specification} of a protected operation.

@dby

An internal call on a protected function shall not occur within:

@xbullet{a precondition expression (see @ref{6.1.1}) of a protected operation;}

@xbullet{the @fa{expression} of a Dynamic_Predicate aspect (see @ref{3.2.4}) of a protected type;}

@xbullet{the @fa{expression} of a Default_Initial_Condition aspect (see @ref{7.3.3}) of a protected type; nor}

@xbullet{a @fa{default_expression} of a @fa{parameter_specification} of a protected operation.}

!corrigendum D.1(8.2/3)

@dinsa

Neither of the Priority or Interrupt_Priority aspects shall be specified for a synchronized interface type.

@dinst

An internal call shall not occur within the @fa{expression} of a Priority or Interrupt_Priority aspect of a protected type.

!corrigendum D.2.6(9.6/5)

@dinsa

The Relative_Deadline aspect shall not be specified on a task or protected interface type. If the Relative_Deadline aspect is specified for a subprogram, the @fa{aspect_definition} shall be a static expression.

@dinst

An internal call shall not occur within the @fa{expression} of a Relative_Deadline aspect of a protected type.

!corrigendum D.16(10/5)

@dinsa

The CPU aspect shall not be specified on a task or protected interface type.

@dinst

An internal call shall not occur within the @fa{expression} of a CPU aspect of a protected type.

!ACATS test

An ACATS B-Test is needed to ensure that internal calls are not allowed in Dynamic_Predicate and Default_Initial_Condition aspects. The test should also check that external calls are allowed.

!appendix

This question was privately raised by Steve Baird.

[a]Shouldn't this be included as example in the RM?

As rare as this usage might be, the need to write it in this way is by far not obvious.