Version 1.3 of ai05s/ai05-0218-1.txt

Unformatted version of ai05s/ai05-0218-1.txt version 1.3
Other versions for file ai05s/ai05-0218-1.txt

!standard 7.5(8.2/3)          11-03-03 AI05-0218-1/02
!class binding interpretation 10-06-13
!status work item 10-06-13
!status received 10-04-15
!priority Low
!difficulty Hard
!qualifier Omission
!subject Generics and volatility
!summary
A nonvolatile generic formal derived type precludes a volatile actual type.
!question
The language tries to prevent an access value from denoting a volatile object of a nonvolatile type.
The following example seems to violate this rule.
procedure Volatility_Test is type T1 is record Field : aliased Integer; end record; type T2 is new T1; pragma Volatile (T2);
generic type Formal_Derived is new T1; package G is X : Formal_Derived; type Ref is access all Integer; Ptr : Ref; end G; package body G is begin Ptr := X.Field'access; end G; package I is new G (T2); begin I.Ptr.all := I.Ptr.all + 1; -- problematic references to a volatile null; -- I.Ptr := I.X.Field'Access; -- clearly illegal end Volatility_Test;
Should this be fixed? (Yes.)
!recommendation
(See Summary.)
!wording
In C.6(12) replace
If an atomic type is used as an actual for a generic formal derived type, then the ancestor of the formal type shall be atomic or allow pass by copy.
with
If an atomic type is used as an actual for a generic formal derived type, then the ancestor of the formal type shall be atomic.
Append after C.6(12):
If a volatile type is used as an actual for a generic formal array type, then the element type of the formal type shall be volatile.
!discussion
We don't want to allow an access value to designate a volatile object of a non-volatile type, but this can happen if we allow (as the language does today) the following scenario:
1) A generic formal type which has an aliased or tagged subcomponent
where the subcomponent does not appear (as seen from the generic body) to be volatile.
2) An instance with a corresponding actual type which is volatile
(which implies that the corresponding actual subcomponent is volatile).
See the example in the !question section for details.
There is no corresponding problem for atomicity because atomicity of a type does not imply atomicity of its components.
There is no problem with formal types other than formal derived and formal array because these are the only formal types that can have tagged or aliased subcomponents (in particular, a discriminant of a formal private type cannot be tagged or aliased),
We are not trying to prevent, for example, a non-volatile formal private type with a corresponding actual type which is volatile.
Since we are really only interested here in volatility, we want to be careful not to disallow useful cases involving atomicity.
For example, we want to allow an atomic array type with a non-atomic component as an actual corresponding to a formal array type.
This probably means that we cannot stick with the "no specific rules for volatility; just reuse the rules for atomicity as per the final sentence of C.6(12/3)" approach.
The proposed rule would be too stringent if we had a way of specifying (e.g. via an aspect clause on a formal type) that a formal type is volatile or has volatile components. If this were possible, then we might need something more permissive like
If a volatile type is used as an actual for a generic formal array type, then the formal array type shall have volatile components.
AARM note: A formal array type has volatile components if the array type is volatile, the element type of the array type is volatile, or the volatile_components aspect of the array type is specified to be True.
This change is incompatible with previous versions of Ada, in that some actual types will no longer be legal for certain formal types. There doesn't seem to be an alternative short of requiring compilers to treat all designated objects of general access types as if they are volatile (which would have a significant negative impact on optimizations). [This affects all general access types because conversions are allowed between access types with the same designated type.]
!corrigendum C.6(12)
Replace the paragraph:
If an atomic object is passed as a parameter, then the type of the formal parameter shall either be atomic or allow pass by copy (that is, not be a nonatomic by-reference type). If an atomic object is used as an actual for a generic formal object of mode in out, then the type of the generic formal object shall be atomic. If the prefix of an attribute_reference for an Access attribute denotes an atomic object (including a component), then the designated type of the resulting access type shall be atomic. If an atomic type is used as an actual for a generic formal derived type, then the ancestor of the formal type shall be atomic or allow pass by copy. Corresponding rules apply to volatile objects and types.
by:
If an atomic object is passed as a parameter, then the type of the formal parameter shall either be atomic or allow pass by copy (that is, not be a nonatomic by-reference type). If an atomic object is used as an actual for a generic formal object of mode in out, then the type of the generic formal object shall be atomic. If the prefix of an attribute_reference for an Access attribute denotes an atomic object (including a component), then the designated type of the resulting access type shall be atomic. If an atomic type is used as an actual for a generic formal derived type, then the ancestor of the formal type shall be atomic. Corresponding rules apply to volatile objects and types.
If a volatile type is used as an actual for a generic formal array type, then the element type of the formal type shall be volatile.
!ACATS Test
Add a B-Test to check that the appropriate actual types are rejected (or modify an existing test).
!ASIS
No ASIS impact.
!appendix

From: Steve Baird
Sent: Thursday, April 15, 2010  7:35 PM

> It seems like the case ought to be detected, or at least we ought to
> consider if the problem is severe enough to fix (as the fix will be
> incompatible).

Ok, I'll inflict this one on the group.

The language tries to prevent an access value from denoting a volatile object of
a nonvolatile type.

The following example seems to violate this rule.
Does this warrant fixing?

   procedure Volatility_Test is
     type T1 is record Field : aliased Integer; end record;
     type T2 is new T1;
     pragma Volatile (T2);

     generic
        type Formal_Derived is new T1;
     package G is
        X : Formal_Derived;
        type Ref is access all Integer;
        Ptr : Ref;
     end G;
     package body G is
     begin
        Ptr := X.Field'Access;
     end G;
     package I is new G (T2);
   begin
     I.Ptr.all := I.Ptr.all + 1; -- problematic references to a volatile
     null;
     -- I.Ptr := I.X.Field'Access; -- clearly illegal
   end Volatility_Test;

Note that C.6(12) currently includes the following:
   ...
   If an atomic type is used as an actual for a generic formal derived
   type, then the ancestor of the formal type shall be atomic or allow
   pass by copy. Corresponding rules apply to volatile objects and
   types.

One solution would be to delete the "or allow pass by copy" wording, although
that change would affect the atomic case as well.

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

From: Randy Brukardt
Sent: Thursday, April 15, 2010  8:03 PM

It should be noted that this rule potentially inflicts a large burden on
implementations that do generic sharing, as it essentially requires anything
whose actual type can be volatile (or atomic) to be passed by copy if that is
allowed at all. For types that allow passing by copy but that the compiler would
normally pass by reference (such as larger composite types), this can be very
expensive. (Alternatively, the compiler could revert to non-sharing semantics
when any actual might be volatile or atomic, but of course that would limit the
sharing possibilities fairly significantly.)

There also seems to be an issue if the compiler choose in the example above to
pass the type by reference in any primitive subprograms. Humm...(answering own
question)...actually T1 int eh example above can't have primitive subprograms
(else the pragma is illegal). So scratch this issue.

Anyway, the sharing issue alone would not be worth changing the language for,
but it seems to provide an additional data point that the existing rule is
dubious.

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

From: Robert Dewar
Sent: Thursday, April 15, 2010  8:08 PM

I am not normally very sympathetic to Randy's concerns about generic sharing,
but in this particular case, I think he has a really good point. This definitely
needs fixing in my view.

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

From: Steve Baird
Sent: Monday, July 19, 2010  6:02 PM

One of my homework items from Valencia is to propose wording for this AI. To
recap, we want prevent an access value from denoting a volatile object of a
nonvolatile type as in the following example:

   procedure Volatility_Test is
     type T1 is record Fld : aliased Integer; end record;
     type T2 is new T1;
     pragma Volatile (T2);

     generic
        type Formal_Derived is new T1;
     package G is
        X : Formal_Derived;
        type Ref is access all Integer;
        Ptr : Ref;
     end G;
     package body G is
     begin
        Ptr := X.Fld'Access;
     end G;
     package I is new G (T2);
   begin
     I.Ptr.all := I.Ptr.all + 1; -- problematic references to a volatile

   end Volatility_Test;

So ...

   !wording

   Append to the end of C.6(12):

     Furthermore, if a volatile type is used as an actual for
     a generic formal derived type, then the ancestor of the formal
     type shall be volatile.

Alternatives include:
    1) In the proposed wording, replace "shall be volatile" with
       "shall be volatile or elementary".  It might appear that there is
       no harm in allowing a generic which has a "new Standard.Integer"
       formal derived type to be instantiated with a volatile actual.
       The problem discussed here has to do with the volatility
       of subcomponents (see final sentence of C.6(8)) and an
       elementary type has no subcomponents. Adding two words to
       allow this case might seem reasonable.

       However, there is another problem which has not been discussed
       which this relaxation might open up.

       4.6(24.9/2) states (in the context of a type conversion between
       two array types unrelated by derivation):
         The operand type of a view conversion shall not have a tagged,
         private, or volatile subcomponent.

       Consider a view conversion between two array types declared in
       a generic body; the two types have our "new Standard.Integer"
       formal derived type as subcomponent of their common element
       type. If we allow a volatile actual type, we can get a
       violation of the above rule and it looks like this could lead
       to problems.

    2) Add no new wording and instead delete the existing phrase
       "or allow pass by copy" from C.6(12). This would disallow
       more than we need to as it would affect the rules for Atomic
       types as well as Volatile types. This seems like a
       significant and unnecessary incompatibility.

    3) Add the new sentence as a new paragraph and drop the leading
       "Furthermore, ". It does seem a odd to have the sentence
          "Corresponding rules apply to volatile objects and types."
       appearing other than at the end of its paragraph.

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

From: Randy Brukardt
Sent: Monday, July 19, 2010  6:24 PM

I didn't think about this in detail, but...

>     2) Add no new wording and instead delete the existing phrase
>        "or allow pass by copy" from C.6(12). This would disallow
>        more than we need to as it would affect the rules for Atomic
>        types as well as Volatile types. This seems like a
>        significant and unnecessary incompatibility.

...this doesn't make sense to me since Atomic always implies Volatile
(C.6(8)) So anything that applies to volatile types also applies to atomic
types, and thus any incompatibility (no matter how it is stated) clearly applies
to atomic as well.

There might be good reasons for not doing this, but the given reason isn't it.

(It should be obvious that the problematic case is even more problematic for
atomic objects, in the event that they are allowed by the implementation.)

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

From: Steve Baird
Sent: Monday, July 19, 2010  6:37 PM

> There might be good reasons for not doing this, but the given reason
> isn't it.

Good point; I had the implication backwards.

I think there are no good reasons for not doing this and the proposed wording
change should simply be that 5-word deletion.

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

From: Steve Baird
Sent: Monday, July 19, 2010  6:49 PM

> ...this doesn't make sense to me since Atomic always implies Volatile
> (C.6(8)) So anything that applies to volatile types also applies to
> atomic types, and thus any incompatibility (no matter how it is
> stated) clearly applies to atomic as well.

I agree with you, but it isn't as obvious as you make it sound.

Consider the case of an atomic formal where the actual is volatile, not atomic,
and allows pass by copy. If this case is possible, then we have a scenario where
the proposed 5-word deletion would be unnecessarily restrictive. But how did we
get this actual which meets all the formal-to-actual matching rules but isn't
atomic? We don't have a "pragma Non_Atomic" which can be applied to a derived
type with an atomic parent.

So I think you are right.

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

From: Tucker Taft
Sent: Monday, July 19, 2010  8:29 PM

You should be careful with misinterpretation of your solution (2), because the
phrase "or allow pass by copy" appears *twice* in C.6(12).  I presume you mean
the second occurrence, which is talking about formal derived types.  And I agree
the most straightforward solution is to eliminate this second "allow pass by
copy".

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

From: Steve Baird
Sent: Tuesday, July 20, 2010  2:56 PM

> You should be careful with misinterpretation of your solution (2),
> because the phrase "or allow pass by copy" appears *twice* in C.6(12).

Good point.

I presume you mean the second
> occurrence, which is talking about formal derived types.

Right.

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

From: Steve Baird
Sent: Thursday, March 3, 2011  4:01 PM

More homework from the Tampa meeting.
Once again, thanks to Randy and Tuck for review and suggestions.

[Editor's note: The wording and discussion sections of version /02
of the AI followed.]

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

From: Randy Brukardt
Sent: Thursday, March 3, 2011  4:01 PM

...
> We are not trying to prevent, for example, a non-volatile formal
> private type with a corresponding actual type which is volatile.

For the record, I had a couple of unresolved concerns related to the above
statement, but I now don't think there is anything that needs to be done.

First, I was concerned that there was a way to create this problem with all
generic formal types using explicitly aliased parameters. That would look
something like:

    generic
        type T is range <>;
        type Acc_T is access T;
    package Gen is
        procedure Proc (P : aliased in T);
    end Gen;

    package body Gen is
        procedure Proc (P : aliased in T) is
           P_A : Acc_T := P'Unchecked_Access;
        begin
           ...
        end Proc;
    end Gen;

If the actual for T was volatile and the designated type of the actual type for
Acc_T was not, a call of Proc with a library-level object would not be erroneous
and there would be a problem.

But I now think the static matching rules combined with the fact that volatile
(and atomic) are type-related aspects prevent problems. The only problems can
occur when the volatility is "inherited" from a larger object, and that requires
components.

[Humm, aside: can a problem occur with an access discriminant? Those can get the
implicit volatility, and have implicit conversions to other types. Naaah,
probably not; they can't be aliased.]

The second issue is the more general one of the impact on generic sharing
implementations. I had originally thought that these rules were intended to
ensure that the compiler always statically knows when it is accessing an atomic
or volatile object (that's important since these properties mainly affect
instruction selection and optimization, not things that can be changed at
runtime). But that is clearly not true when generic sharing is being used.

For instance, in the example above, a sharing implementation will not know
whether or not T is a volatile (or atomic) type. The effect is that pretty much
every object of a formal type (or a type derived from a formal type) has to
assume the worst, use thunks to do all reads and writes (because of the
possibility of atomic operations, for which reading or writing too many bits
is also not allowed), and block the majority of optimizations.

I've been pondering just how much of an additional hit (beyond the existing
costs of sharing) that this really is. For composite types (formal private
types, formal array types, and most formal derived types) it probably is
negligible as the fact that the size of objects isn't known until runtime forces
virtually all operations to memory (can't put anything in a register which might
not be big enough). For elementary types, the additional cost could be as much
as 10-20%, but mostly from the loss of common subexpression optimizations on
reads and dead write optimizations. (And of course that is very code dependent.)
The use of thunks is already required to deal with the representation of aliased
objects/components properly, so there is no additional cost from that.

The biggest danger is probably from future optimizations that forget that
generic bodies can't be optimized much. But that's clearly my problem and not a
language problem.

Since a language change to have better guarantees would be fairly incompatible
for volatile and atomic types, I think there would have to be more of an
implementation problem to push for a change here. So I won't do that.

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

From: Randy Brukardt
Sent: Saturday, March 5, 2011  12:33 AM

> More homework from the Tampa meeting.

There was a part of the notes (actually two parts) from the meeting that you
didn't address:

"Also note that tagged components would have the same problem."

I'm not sure what this means, as tagged components are not automatically aliased.
So I suspect this is just junk. However:

"Steve previously noticed a problem with view conversions of arrays (that is, in
an in out parameter) that prevents volatile components."

I'm not sure what this means, either (I suspect this is misrecorded), but there
clearly is a problem here.

The parameter passing rule currently reads:

"If an atomic object is passed as a parameter, then the formal parameter shall
either have an atomic type or allow pass by copy."

Atomic_Components can be applied to an anonymous array object. The array object
as a whole is not an atomic object by C.6(7/2) (the individual components are).
So the above mentioned rule does not apply to such an anonymous array object.
There does not appear to be any rule preventing converting that anonymous array
object to some other array type (including one that does not have atomic components).
Thus we can cause the original problem with such an array object:

   procedure AA_Test is
     type A1 is array (1 .. 10) of aliased Integer;
     O1 : array (1 .. 10) of aliased Integer;
     pragma Atomic_Components (O1);

     generic
        type Arr is array (1 .. 10) of aliased Integer;
     package G is
        type Ref is access all Integer;
        Ptr : Ref;
        procedure P (A : Arr);
     end G;
     package body G is
     begin
        procedure P (A : aliased Arr) is
        begin
            Ptr := A(1)'Unchecked_Access;
        end P;
     end G;
     package I is new G (A1);
   begin
     I.P (A1(O1));
     I.Ptr.all := I.Ptr.all + 1; -- problematic references to a volatile
   end Volatility_Test;

[Note that I used "aliased" in the parameter to ensure by-reference passing;
most compilers would pass this type by reference anyway so that isn't strictly
necessary. This code is not erroneous so long as the passed object lives as long
as the instance.]

Probably this conversion ought to be illegal, but I'm not sure of the exact rule
needed.

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

Questions? Ask the ACAA Technical Agent