!standard C.6(12) 11-03-03 AI05-0218-1/02 !class binding interpretation 10-06-13 !status Amendment 2012 11-03-24 !status ARG Approved 8-0-0 11-03-17 !status Amendment 2012 11-03-03 !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) @drepl 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 @b, then the type of the generic formal object shall be atomic. If the @fa of an @fa 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. @dby 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 @b, then the type of the generic formal object shall be atomic. If the @fa of an @fa 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. ****************************************************************