!standard C.6(6.1/3) 20-04-27 AI12-0282-1/05 !standard C.6(6.3/3) !standard C.6(6.5/3) !standard C.6(6.8/3) !standard C.6(12/3) !standard C.6(12.1/3) !standard C.6(21/4) !class Amendment 18-06-05 !status Amendment 1-2012 19-03-11 !status work item 20-03-04 !status ARG Approved 10-0-0 19-03-11 !status work item 18-06-05 !status received 18-05-19 !priority Low !difficulty Easy !subject Atomic, Volatile, and Independent generic formal types !summary Aspects Atomic, Volatile, Independent, Atomic_Components, Volatile_Components, and Independent_Components are specifiable for generic formal types. The actual type must have a matching specification. We continue to allow actual types to be atomic/volatile/independent without the formal type being as well, except in special cases of formal array types and formal derived types. We introduce an implementation permission related to this situation. !problem One of the most important additions to Ada 2012 is the support for contracts. A contract allows the creator of an abstraction to better specify the intended use of the abstraction. Certain aspects such as Nonblocking can be specified for generic formal types, which allows making more precise assumptions about the actuals within the body of the generic. Currently the Atomic, Atomic_Components, Volatile, Volatile_Components, Independent, and Independent_Components aspects cannot be specified for a generic formal type. To enable programmers to create generic abstract data types that can be updated atomically or treated as volatile objects, Ada should allow the aspects Atomic, Atomic_Components, Volatile, Volatile_Components, Independent, and Independent_Components to be specified on a generic formal type. We have to be careful not to introduce an incompatibility here, because atomic (and volatile) types are currently permitted as generic actuals for formal types that are not specified as atomic (or volatile). !proposal (See Summary.) !wording Modify C.6(6.1/3): -- to allow aspect Atomic, Volatile, or Independent -- to be applied to a generic formal type For an object_declaration, a component_declaration, [or] a full_type_declaration{, or a formal_complete_type_declaration}, the following representation aspects may be specified: Modify C.6(6.5/3): -- to allow Atomic_Components, Volatile_Components, and -- Independent_Components to be applied to a generic -- formal array type; also make existing wording -- more consistent with how aspect Pack is defined. For a full_type_declaration of an array type{,} [(including the anonymous type of] an object_declaration [of an anonymous array object)]{for an object of an anonymous array type, or the formal_complete_type_declaration of a formal array type}, the following representation aspects may be specified: Modify C.6(6.8/3): -- Allow Independent_Components on a generic formal -- composite type; also make existing wording -- more consistent with how aspect Pack is defined. For a full_type_declaration {of a composite type,} [(including the anonymous type of] an object_declaration [of an anonymous array object)]{for an object of an anonymous composite type, or the formal_complete_type_declaration of a formal composite type}, the following representation aspect may be specified: Modify C.6(12/3): If an atomic object is passed as a parameter, then the formal parameter shall either have an atomic type or allow pass by copy. 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 [Redundant: (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]. Modify C.6(12.1/3): {If the Atomic, Atomic_Components, Volatile, Volatile_Components, Independent, or Independent_Components aspect is True for a generic formal type, then that aspect shall be True for the actual type.} 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. {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. A corresponding rule applies to volatile types.} Add after C.6(21/4): Implementation Permissions Within the body of an instance of a generic unit that has a formal type T that is not atomic and an actual type that is atomic, if an object O of type T is declared and explicitly specified as atomic, the implementation may introduce an additional copy on passing O to a subprogram with a parameter of type T. !discussion For uniformity, we have allowed Independent and Independent_Component aspects on generic formal types, even though the capability is not as critical as that for atomic and volatile. However, independent addressability becomes more important as we move toward more parallelism, since two logical threads can concurrently manipulate different components of the same object only if they are independently addressable. We considered whether Atomic_Components should be allowed or not. With the new capabilities of AI12-0234-1, conceivably one might want to apply atomic operations to elements of an array, such as an array of counters. The point of the atomic operation libraries of AI12-0234-1 is to allow programmers to create useful abstractions, and so Atomic_Compenents was allowed. This left Volatile and Volatile_Components. There is less of a need to support these as generic formals, but since they are closely related to atomic types it seemed somewhat better to allow these, which also fit in better with the wording in the RM. We adjusted some of the existing wording, as it was written during the massive switch from pragmas to aspects, and was a bit awkward (e.g. "including the anonymous type of an object_declaration of an anonymous array object" doesn't really make sense, because the array object is not anonymous, and the declaration doesn't have a type, since it is the declared object that has an anonymous type). To ensure compatibility, we allow actual types with additional atomicity, volatility, or independence to match formal types without such additional characteristics, except in special cases relating to formal array types and formal derived types. Generic formal private types are often used as "universal" types, and it would be annoying to not allow atomic types to match them. For instance, Sequential_IO would not work with an atomic type given these rules. We provide an implementation permission to introduce an extra copy when passing an object declared inside a generic that is explicitly specified as atomic, in the case where the formal type is nonatomic but the actual type is atomic. Without this permission, the decision of whether to make the extra copy would need to differ depending on the atomicity of the actual type, which makes sharing generic instances more difficult. We do not believe these extra copies will create problems in any realistic program. Arguably, any program that can tell the difference is unusual (if not pathological): so long as every operation is properly atomic, copying or lack thereof when making a call to a routine with the same parameter type shouldn't matter. !corrigendum C.6(6.1/3) @drepl For an @fa, a @fa, or a @fa, the following representation aspects may be specified: @dby For an @fa, a @fa, a @fa, or a @fa, the following representation aspects may be specified: !corrigendum C.6(6.5/3) @drepl For a @fa of an array type (including the anonymous type of an @fa of an anonymous array object), the following representation aspects may be specified: @dby For a @fa of an array type, an @fa for an object of an anonymous array type, or the @fa of a formal array type, the following representation aspects may be specified: !corrigendum C.6(6.8/3) @drepl For a @fa (including the anonymous type of an @fa of an anonymous array object), the following representation aspect may be specified: @dby For a @fa of a composite type, an @fa for an object of an anonymous composite type, or the @fa of a formal composite type, the following representation aspect may be specified: !corrigendum C.6(12/3) @drepl If an atomic object is passed as a parameter, then the formal parameter shall either have an atomic type or allow pass by copy. 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. @dby If an atomic object is passed as a parameter, then the formal parameter shall either have an atomic type or allow pass by copy. 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. Corresponding rules apply to volatile objects. !corrigendum C.6(12.1/3) @drepl 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. @dby If the Atomic, Atomic_Components, Volatile, Volatile_Components, Independent, or Independent_Components aspect is True for a generic formal type, then that aspect shall be True for the actual type. 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. 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. A corresponding rule applies to volatile types. !corrigendum C.6(21/4) @dinsa If the Pack aspect is True for a type any of whose subcomponents are atomic, the implementation shall not pack the atomic subcomponents more tightly than that for which it can support indivisible reads and updates. @dinst @s8<@i> Within the body of an instance of a generic unit that has a formal type @i that is not atomic and an actual type that is atomic, if an object @i of type @i is declared and explicitly specified as atomic, the implementation may introduce an additional copy on passing @i to a subprogram with a parameter of type @i. !ASIS TBD. !ACATS test ACATS B-Tests and C-Tests. !appendix From: Brad Moore Sent: Friday, May 18, 2018 11:27 PM Here is one of the offspring of AI12-0234-1 (Compare and Swap atomic operations) [This is version /01 of this AI - Editor.] This AI specifically is to allow Atomic, Atomic_Components, Volatile, and Volatile_Components to be specified on a generic formal type declaration. AI12-0234-1 depends on this AI. It really only needs to allow Atomic to be specified on generic formal types, but allowing Atomic_Components seemed useful also, since one can easily imagine programmers creating abstractions with the packages defined in AI12-0234-1, that operate on arrays. Given those two aspects, it seems natural to also allow Volatile and Volatile_Components, even though it is not as easy to think of cases where these two aspects would be useful, but allowing them seemed to fit better into the language, than leaving them out. The aspects Independent and Independent_Components were left out because it was quite a bit even more difficult to think of possible use for these, and those two aspects seem less coupled to Atomic than Volatile, so it was easier to leave them out. **************************************************************** From: Jeff Cousins Sent: Monday, June 11, 2018 8:36 AM A few typos: !problem To enable programmers to create generic abstraction data types that {can} be updated atomically or treated as {a} volatile object, should Ada allow the aspects !discussion We considered whether other related aspects such as Independent, or Independent_Components{,} should also be allowed to be specified for generic formal or We considered whether other related aspects such as Independent[,] or Independent_Components should also be allowed to be specified for generic formal We decided against that because it wasn't clear that such a capability {could conceivably be} [would be conceivably] useful. somewhat better to allow these, which also fit{s} in better with the wording in the RM. or somewhat better to allow these, which {would} also fit in better with the wording in the RM. **************************************************************** From: Tucker Taft Sent: Thursday, March 7, 2019 4:09 PM Here is an update to AI12-0282-1, about applying the shared-variable aspects to generic formal types. The update incorporates the the changes we discussed during the last ARG phone meeting. I also discovered that some of the existing wording in C.6 didn't make a lot of sense, so I cleaned that up a bit. [This is version /02 of the AI - Editor.] **************************************************************** From: Randy Brukardt Sent: Thursday, March 7, 2019 6:55 PM > Here is an update to AI12-0282-1, ... > For [a full_type_declaration] {the full type declaration of a > composite type} (including the anonymous type [of] {defined by a full > type definition within} an object_declaration [of an anonymous array > object]){, or the formal_complete_type_declaration of a formal > composite type}, the following representation aspect may be specified: According to both 3.2.1 and index, we have "full type definition" and "full_type_declaration", but not "full type declaration". I realize that we sometimes informally use terms without the underscores, but in such cases they mean the same thing as the syntax term. Sometimes we define the English terms to mean something different (as in "function call" vs. "function_call"), but let's not go there again. And it's pretty clear from the RM that an object_declaration with an anonymous array type is never a full_type_declaration (or full type declaration, for that matter). It *is* a "full type definition", but those don't have aspects. There's no way syntactically to specify an aspect for an anonymous type. The aspect is *defined* for an object (of an anonymous array type), and *applies* to the anonymous type of the object. (It's impossible to give an aspect on an anonymous array definition, there's no syntax for that.) I also realize that I probably was the original source of this bug (I vaguely recall a discussion like this years ago), but in any case, if we're going to fix this, we need to do it right. Otherwise, replacing one set of wavy-gravy wording with another isn't helping anything. So I think the lead-in has to say something like: For the full_type_declaration of a composite type, the formal_complete_type_declaration of a formal composite type, or an object_declaration with an array_type_definition (in this case, the aspect specified is that of the anonymous array type of the type definition, not of the object), the following representation aspect may be specified: We have to talk about the aspect being specified on an object_declaration ('cause that's where it is specified) and applying to the type (because that's where it's meaningful). Thoughts, improvements?? **************************************************************** From: Tucker Taft Sent: Thursday, March 7, 2019 7:26 PM > According to both 3.2.1 and index, we have "full type definition" and > "full_type_declaration", but not "full type declaration". I realize > that we sometimes informally use terms without the underscores, but in > such cases they mean the same thing as the syntax term. Sometimes we > define the English terms to mean something different (as in "function call" > vs. "function_call"), but let's not go there again. I based my usage on the Pack aspect, 13.2(5/5): "{AI05-0229-1} For a full type declaration of a composite type, the following language-defined representation aspect may be specified: ..." Here is an Editor's Note from AI05-0229-1, regarding this usage: "[Editor's Note: We say "full type declaration" in order to encapsulate all of the rules about requiring a "first subtype" and not a partial view. This is somewhat redundant with the rules in 13.1 for representation aspects, but it seems good to be clear.]" I also found "declaration of a full type" in RM 3.2.1 (8/2) which seemed to imply the slight re-ordering of terms, which is equivalent at least in English, of "full type declaration" would cover the case of a type defined as part of an object declaration: "A named type that is declared by a full_type_declaration, or an anonymous type that is defined by an access_definition or as part of declaring an object of the type, is called a full type. The declaration of a full type also declares the full view of the type. ..." Anyway, it doesn't hurt to be explicit, but I was trying to piggyback on the Pack aspect, as that seemed closest to something like Independent_Components (in fact, they are almost direct opposites of each other, though Independent_Components overrides Pack, presumably). > And it's pretty clear from the RM that an object_declaration with an > anonymous array type is never a full_type_declaration (or full type > declaration, for that matter). It *is* a "full type definition", but > those don't have aspects. There's no way syntactically to specify an > aspect for an anonymous type. Well I wouldn't quite agree, as the paragraph quoted above seems to be implying an object declaration that includes a full type definition is "declaring a full type." > The aspect is *defined* for an object (of an anonymous array type), > and *applies* to the anonymous type of the object. (It's impossible to > give an aspect on an anonymous array definition, there's no syntax for > that.) > > I also realize that I probably was the original source of this bug (I > vaguely recall a discussion like this years ago), but in any case, if > we're going to fix this, we need to do it right. Otherwise, replacing > one set of wavy-gravy wording with another isn't helping anything. > > So I think the lead-in has to say something like: > > For the full_type_declaration of a composite type, the > formal_complete_type_declaration of a formal composite type, or an > object_declaration with an array_type_definition (in this case, the > aspect specified is that of the anonymous array type of the type > definition, not of the object), the following representation aspect > may be specified: This seems to be a bit of overkill. I'll try again before our meeting. > We have to talk about the aspect being specified on an > object_declaration ('cause that's where it is specified) and applying > to the type (because that's where it's meaningful). > > Thoughts, improvements?? I think we can do better, but I am too busy to focus on this right now. **************************************************************** From: Tucker Taft Sent: Thursday, March 7, 2019 5:44 PM I looked into the action item assigned to me, which was to answer the the question I raised during the meeting (during discussion of AI12-0282) about whether the second-to-last sentence in C.6(12/3): "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" needs to be addressed by a similar rule for passing a type with aspect Atomic_Components. My conclusion is that, yes, a similar rule is needed. I discussed this a bit with Steve, and he formulated the following fairly concise example that illustrates the need. procedure ac is type Ac_String is new String with Atomic_Components; generic type Str is new String; package G1 is X : Str := "abc"; end G1; generic Ch : in out Character; package G2 is end G2; package body G1 is package I2 is new G2 (Ch => X (1)); end G1; package I1 is new G1 (Ac_String); -- Legal? begin null; end; The instantiation I1 seems to be allowed by current rules, but then the expanded generic body has a violation of the rule that if the actual for a formal in out object is atomic, then the formal object must be declared atomic (second sentence of C.6(12/3)). So it seems that we will need a new AI to address this. I'm assuming that I wasn't also expected to draft such a new AI as part of my homework, but if that was expected, then sorry about that, as I haven't done that and have run out of time. One other somewhat related thing I'll also note here is that it looks like the last sentence of C.6(12/3), that talks about corresponding rules applying to volatile objects and types, may need to be revised to include mention of Volatile_Components. **************************************************************** From: Tucker Taft Sent: Thursday, March 7, 2019 7:37 PM > So it seems that we will need a new AI to address this. I'm assuming > that I wasn't also expected to draft such a new AI as part of my > homework, but if that was expected, then sorry about that, as I > haven't done that and have run out of time. I think we can address this as part of AI12-0282-1, as it is making a number of minor fixes to C.6 along the way. > One other somewhat related thing I'll also note here is that it looks > like the last sentence of C.6(12/3), that talks about corresponding > rules applying to volatile objects and types, may need to be revised > to include mention of Volatile_Components. This should also be something we can address in AI12-0282-1. **************************************************************** From: Randy Brukardt Sent: Thursday, March 7, 2019 8:25 PM > I think we can address this as part of AI12-0282-1, as it is making a > number of minor fixes to C.6 along the way. That's fine, but who's going to write it? You said that you don't have time, and I sure don't have time in the two hours either. We either can put AI12-0282-1 on the agenda as it is, or leave it off completely for a rewrite to add Gary's stuff. And then we have the question of how/when it would get addressed at all. I don't know if I have the stamina for another meeting, and I know that we're getting well beyond the meeting effort that I budgeted last fall. And I have lots of RM work to do still, which doesn't get done messing with meeting minutes. This thing is supposed to be done in 3 weeks! **************************************************************** From: Tucker Taft Sent: Thursday, March 7, 2019 8:48 PM I don't have time tonight, but tomorrow afternoon I should have time to work on it. There are plenty of other AIs presumably for folks to review in the mean time. **************************************************************** From: Tucker Taft Sent: Friday, March 8, 2019 1:26 PM Here is the next iteration of this AI. [This is version /03 of the AI - Editor.] **************************************************************** From: Randy Brukardt Sent: Friday, March 8, 2019 6:55 PM Thanks for doing this. **************************************************************** From: Tucker Taft Sent: Thursday, February 27, 2020 11:27 AM We just bumped into a customer affected by the changes in C.6(12/5) that "generalized" the requirement of matching between generic actual and formal types with respect to atomic/volatile/etc. The problem is that there is existing code that does instantiations where the actual is atomic, but the formal is something like "limited private." In generalizing the requirement for matching, we didn't properly account for this incompatibility. In looking at the changes in C.6, I suggest we revert the changes to C.6(12/5), while preserving the changes to C.6(12.1/5). The argument in favor of the "generalization" in C.6(12/5) is weak: Now that these aspects can be specified on generic formal types, we can generalize the requirement that in a generic instantiation, if one of the atomic or volatile aspects applies to the actual type, it shall also apply to the formal type. In general we need to know inside a generic wherever we might be producing atomic or volatile objects, since there are special rules about them that affect parameter passing. and makes no mention of the incompatibility created by this generalization. So I suggest C.6(12/5) goes back to: If an atomic object is passed as a parameter, then the formal parameter shall either have an atomic type or allow pass by copy. 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. We could actually *relax* the derived type requirement to: ... 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. ... That is, so long as the formal is atomic, either because it is derived from an atomic ancestor, *or* because an explicit aspect Atomic makes it atomic, we are OK. **************************************************************** From: Randy Brukardt Sent: Thursday, February 27, 2020 3:28 PM ... > The argument in favor > of the "generalization" in C.6(12/5) is weak: > > Now that these aspects can be specified on generic formal types, we can > generalize the requirement that in a generic instantiation, if one of > the atomic or volatile aspects applies to the actual type, it shall also > apply to the formal type. In general we need to know inside a generic > wherever we might be producing atomic or volatile objects, since there > are special rules about them that affect parameter passing. It's not "weak", it's critical if one wants any sharing of generics with generic formal private types. If one has to support multiple parameter passing models when calling between routines inside the generic unit, then supporting sharing becomes impractical. (Yes, this can be detected without involving atomics, but any such code is pathological, whereas for atomics it's critical to their semantics.) A code sharing implementation would never (intentionally) support matching an atomic in this way; certainly Janus/Ada will not. So the incompatibility has always existed, it's just been latent because of template implementations. I note that without sharing of generic formal private types, generic sharing becomes essentially useless. (It's already impractical to share most other formal types because of issues with "aliased".) Only dubious programs (those that instantiate the same generic with the same parameters) could ever benefit from sharing, and that is way too few cases to bother with. So to me, repealing this rule is the same as banning sharing (especially in the face of the DEC patent, which means that only very deep pockets can use partial sharing). If we're going to do that, let's do it intentionally (and with a sweep to remove all rules that primarily benefit sharing) rather than as the side-effect of some people with badly designed code. **************************************************************** From: Tucker Taft Sent: Thursday, February 27, 2020 7:47 PM ... > A code sharing implementation would never (intentionally) support > matching an atomic in this way; certainly Janus/Ada will not. So the > incompatibility has always existed, it's just been latent because of > template implementations Can you provide more of an example? I am suggesting only that we go back to the Ada 2012 rule. I don't see the justification for introducing this very significant incompatibility. > I note that without sharing of generic formal private types, generic > sharing becomes essentially useless. (It's already impractical to > share most other formal types because of issues with "aliased".) Only > dubious programs (those that instantiate the same generic with the > same parameters) could ever benefit from sharing, and that is way too few > cases to bother with. > > So to me, repealing this rule is the same as banning sharing > (especially in the face of the DEC patent, which means that only very > deep pockets can use partial sharing). If we're going to do that, > let's do it intentionally (and with a sweep to remove all rules that > primarily benefit sharing) rather than as the side-effect of some people > with badly designed code. I suspect you might be overreacting. I am not suggesting we "repeal" a rule, merely that we preserve it as it was in Ada 2012, where it only applied to formal derived types. Generalizing the matching to say that you cannot pass an actual atomic type to a formal private type is very incompatible. It really means there is *no* universal formal type. We use formal private types in many places expecting them to match *anything* non-limited, e.g. in Unchecked_Deallocation and Unchecked_Conversion. Suddenly not allowing atomic or volatile types to be used in instantiations of things like Unchecked_Deallocation and Unchecked_Conversion is one of the bigger incompatibilities I can imagine. **************************************************************** From: Tucker Taft Sent: Thursday, February 27, 2020 7:55 PM > ... We use formal private types in many places expecting them to match > *anything* non-limited, e.g. in Unchecked_Deallocation and > Unchecked_Conversion. Correction: Both of these cases use limited private types. In any case, the same issue applies if exact matching of atomic/volatile is required. **************************************************************** From: Randy Brukardt Sent: Tuesday, March 3, 2020 12:30 AM ... > > If one has to support multiple parameter passing models when calling > > between routines inside the generic unit, then supporting sharing > > becomes impractical. (Yes, this can be detected without involving > > atomics, but any such code is pathological, whereas for atomics it's > > critical to their > > semantics.) > > > > A code sharing implementation would never (intentionally) support > > matching an atomic in this way; certainly Janus/Ada will not. So the > > incompatibility has always existed, it's just been latent because of > > template implementations > > Can you provide more of an example? I am suggesting only that we go > back to the Ada 2012 rule. I don't see the justification for > introducing this very significant incompatibility. AARM C.6(19.b) concisely explains the point: "It has to be known at compile time whether an atomic or a volatile parameter is to be passed by copy or by reference." That's necessarily not true in a shared generic if you don't know if the formal type is instantiated with an atomic or non-atomic type. Let's look at the issue, ignoring Atomic for the moment. Consider the following generic: generic type Priv is private; package Gen is procedure Foo (P : in out Priv); end Gen; package body Gen is procedure Bar (P : in out Priv) is begin ... end Bar; procedure Foo (P : in out Priv) is begin ... Bar (P); -- (1) end Foo; end Gen; package Inst is new Gen (Natural); -- (2) V : Natural; Inst.Gen.Foo (V); -- (3) Janus/Ada uses reference passing for all generic private types, as that is generally required for almost all actual types. We "fake" by-copy passing at the interface to the generic for by-copy types (as Natural is here). So there is a copy of V at (3). However, we do not try to fix internal calls in this way. So the call at (1) does not make a copy of P. Robert Eachus is the one who pointed out to me that this is technically wrong, back at some early 1990s DR meeting. However, any generic that assuming by-copy passing at (1) is necessarily assuming something that is not guaranteed by the contract of the generic, and something that cannot be specified in any form of contract that Ada provides (there's no "By_Copy" attribute). As such, any such generic is a pathology; the effects on objects viewed from outside of the generic are correct, and whatever happens inside the generic shouldn't matter for the correctness of the generic. However, Atomic is different in this respect: C.6(20/5) requires all of the reads and writes of atomics to be part of the external effect of a program. Thus, failing to copy the object at (1) would simply be wrong (and in a way that matters for correctness in the face of multiple tasks). > > I note that without sharing of generic formal private types, generic > > sharing becomes essentially useless. (It's already impractical to > > share most other formal types because of issues with "aliased".) > > Only dubious programs (those that instantiate the same generic with > > the same parameters) could ever benefit from sharing, and that is > > way too few cases to bother with. > > > > So to me, repealing this rule is the same as banning sharing > > (especially in the face of the DEC patent, which means that only > > very deep pockets can use partial sharing). If we're going to do > > that, let's do it intentionally (and with a sweep to remove all > > rules that primarily benefit sharing) rather than as the side-effect > > of some people with badly designed code. > > I suspect you might be overreacting. Slightly, in that I've realized (for the first time, even though I've known about this problem for 27+ years) that there is a (very expensive) solution. One could have a by-copy flag in the sharing data and make a copy at each and every call site; it's expensive because the size of the actual is unknown when the generic is compiled and thus one would need to use some form of dynamic allocation to create the temporary at the call sites (moreover, that code would be in every generic even though it would be rarely used). > I am not suggesting we > "repeal" a rule, merely that we preserve it as it was in Ada 2012, > where it only applied to formal derived types. My point is that the Ada 2012 was "wrong", in the sense that it violates C.6(19.b) for shared generics. And if we're going to say that doesn't matter, then we're saying that sharing itself doesn't matter. If we're going to say that, we shouldn't be doing so implicitly. Moreover, this is the only kind of formal parameter that is (or should I say WAS) readily sharable. All of the other kinds of formal parameters have really nasty issues that make them much less sharable than appears on the surface. (One can somewhat mitigate that if one could follow the technique covered by the DEC patents of having separate bodies for each possible representation, but using that requires either having no commercial interest or having lots of money to defend infringements against dubious patents.) > Generalizing the matching to say that you cannot pass an actual atomic > type to a formal private type is very incompatible. It really means > there is *no* universal formal type. We use formal private types in > many places expecting them to match *anything* non-limited, e.g. in > Unchecked_Deallocation and Unchecked_Conversion. Suddenly not > allowing atomic or volatile types to be used in instantiations of > things like Unchecked_Deallocation and Unchecked_Conversion is one of > the bigger incompatibilities I can imagine. In the case of Unchecked_Conversion, it seems like a feature. :-) And I would expect problems with putting atomic objects in the typical heap implementations of today. (Not necessarily a problem on embedded systems, of course). Anyway, an effect of the Ada contract model is that there aren't universal formal types. We have many places where we don't have universal matching (array and access parameters both have issues that way), and it seems to me that we are starting to weaken that contract model substantially if we expect there to be a universal formal type of any kind. I think we need to decide whether we care about the contract model and whether we care about sharing (the former mainly enables the latter). If not, then a C++-like model makes more sense. And if so, we have to realize that "universal" formal types is not a sane thing to support. (I don't expect this to be the only such problem.) **************************************************************** From: Tucker Taft Sent: Tuesday, March 3, 2020 3:50 AM I understand your point, but this has been this way for 8 years, so compatibility is a pretty big deal at this point, and we have already encountered a customer who has bumped into this issue, which is surprising because there are so few customers who have tried the Ada 202X features. Who knows how many will bump into this once more folks try the Ada 202X features. In any case, this AI makes no mention of this incompatibility, and so I believe we need to re-open the AI to incorporate the compatibility discussion and make a decision with that being part of our consideration. I think the DEC issue is a red herring now, since that compiler effort is dead. I think for a generic sharing compiler, you could have the standard implementation which does what your compiler currently does, and then have one other implementation which assumes the worst about actual types being atomic and/or volatile. It seems unlikely that you would need anything in the middle. In the short-term you could generate a warning if a customer instantiated a formal type with an atomic type. **************************************************************** From: Randy Brukardt Sent: Tuesday, March 3, 2020 5:31 PM > I understand your point, but this has been this way for 8 years, so > compatibility is a pretty big deal at this point, and we have already > encountered a customer who has bumped into this issue, which is > surprising because there are so few customers who have tried the Ada > 202X features. Who knows how many will bump into this once more folks > try the Ada 202X features. In any case, this AI makes no mention of > this incompatibility, and so I believe we need to re-open the AI to > incorporate the compatibility discussion and make a decision with that > being part of our consideration. Agreed that we need to discuss this. I'm mainly pointing out that it is not open-and-shut as to what to do. > I think the DEC issue is a red herring now, since that compiler effort > is dead. Sadly, that's when patents are the most dangerous. Patent leeches buy them up for next to nothing with the sole intention of extracting money from people who are actually building stuff. One needs $$$ to either pay the blackmailers or to force the patents to be invalid. It's a serious risk for any software development. > I think for a generic sharing > compiler, you could have the standard implementation which does what > your compiler currently does, and then have one other implementation > which assumes the worst about actual types being atomic and/or > volatile. It seems unlikely that you would need anything in the > middle. In the short-term you could generate a warning if a customer > instantiated a formal type with an atomic type. A multi-body approach wouldn't work with the current Janus/Ada implementation (which is very much one-pass, straight Dragon book, outside of expressions). If I was going to change that to allow multiple bodies, it would be by converting much (or maybe all) of the remaining Dragon book design to a tree-based design, probably with much of the code generation being deferred to bind-time. With such a design, sharing would become unnecessary and really would not buy enough to bother with outside of sharing identical instances that don't have package data (those are fairly common, and would require no special code generation). That's a massive job, of course (I spent almost three months just restructuring expression processing to support aspect clauses, and this would be a much larger job). So don't expect it to happen any time soon, modulo my winning the Powerball. **************************************************************** From: Tucker Taft Sent: Wednesday, March 4, 2020 5:42 AM > ... > > That's a massive job, of course (I spent almost three months just > restructuring expression processing to support aspect clauses, and > this would be a much larger job). So don't expect it to happen any > time soon, modulo my winning the Powerball. It sounds like if we decide to revert to the 2012 rule, then you should just emit a warning. The actual likelihood of a real problem is very small. The business in C.6(19) about passing by copy if the actual is atomic and the formal is not would not arise for a formal private type, since the type is private and so you couldn't get into the situation where the actual and formal would disagree. To be discussed further at an ARG meeting, I presume... **************************************************************** From: Randy Brukardt Sent: Wednesday, March 4, 2020 6:29 PM > > ... > > > > That's a massive job, of course (I spent almost three months just > > restructuring expression processing to support aspect clauses, and > > this would be a much larger job). So don't expect it to happen any > > time soon, modulo my winning the Powerball. > > It sounds like if we decide to revert to the 2012 rule, then you > should just emit a warning. The actual likelihood of a real problem > is very small. The business in C.6(19) about passing by copy if the > actual is atomic and the formal is not would not arise for a formal > private type, since the type is private and so you couldn't get into > the situation where the actual and formal would disagree. Sure, but all formal private types are passed by reference. So if the actual type was defined to be by-copy, that would not happen on internal calls. That clearly would matter in some cases; there would be more or less operations on the atomic object itself than would occur from the canonical semantics (even after applying 11.6 permissions). I agree that it wouldn't be very common (it would mainly be noticeable on objects that are declared inside of the generic with the formal type, since outside objects by definition would have been copied when entering the generic), but can you make a case that such uses are pathological? So this is one of these cases where being subtly different is OK, at least until someone runs into a case where it is not. This latter possibility bothers me when I don't have an argument for pathology, since the alternative is a lot of distributed overhead. **************************************************************** From: Randy Brukardt Sent: Wednesday, March 4, 2020 11:26 PM > So I suggest C.6(12/5) goes back to: > > If an atomic object is passed as a parameter, then the formal > parameter shall either have an atomic type or allow pass by copy. 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. I just noticed that this suggestion has a major problem. Specifically, it is missing matching rules for all of the generic formal types that *are* declared atomic. The rewording required exact matching of atomicity, but even if we were to repeal that for most formal types, we *still* need to require that the actual type is atomic if the formal type is atomic. [Yikes! The original wording did *not* require exact matching of atomicity, even though that was the clear intent. I suppose that is why you failed to preserve the critical requirement. Sigh.] > We could actually *relax* the derived type requirement to: > > ... 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. ... > > That is, so long as the formal is atomic, either because it is derived > from an atomic ancestor, *or* because an explicit aspect Atomic makes > it atomic, we are OK. We need a rule like this for every formal type, because otherwise one could pass a non-atomic type to a declared atomic formal -- which would defeat the purpose of declaring it in the first place. (We wanted to require passing atomic types to the new atomic operation generics.) I think at a minimum we need to turn around the original rule: If a generic formal type is atomic, then the actual type shall be atomic. And then we need no special rule for generic formal derived types. I suspect that most of the ARG thought this was what the wording was attempting to do (although the discussion seems to suggest that an exact match was intended). **************************************************************** From: Tucker Taft Sent: Thursday, March 5, 2020 4:03 AM >Sure, but all formal private types are passed by reference. So if the actual >type was defined to be by-copy, that would not happen on internal calls. >That clearly would matter in some cases; there would be more or less >operations on the atomic object itself than would occur from the canonical >semantics (even after applying 11.6 permissions). I agree that it wouldn't >be very common (it would mainly be noticeable on objects that are declared >inside of the generic with the formal type, since outside objects by >definition would have been copied when entering the generic), but can you >make a case that such uses are pathological? I didn't mean to claim it was pathological, but merely for the purposes of RR's compiler in the near term, you could provide a warning. I also wonder what your compiler would do when the formal private type has "with atomic" on it. You still don't know whether the actual is a by-copy or a by-reference type, so it seems like you would still have to have multiple expansions, or have a run-time switch to do a copy if the actual turns out to be a by-copy type. >So this is one of these cases where being subtly different is OK, at least >until someone runs into a case where it is not. This latter possibility >bothers me when I don't have an argument for pathology, since the >alternative is a lot of distributed overhead. If you have a customer who can detect this subtlety, I would have to imagine they have a rather bizarre setup, which is probably pretty fragile in other ways. And as asked above, how will you handle the situation if "with Atomic" is on the formal type? **************************************************************** From: Randy Brukardt Sent: Thursday, March 5, 2020 11:25 PM ... >>Sure, but all formal private types are passed by reference. So if the >>actual type was defined to be by-copy, that would not happen on internal calls. >>That clearly would matter in some cases; there would be more or less >>operations on the atomic object itself than would occur from the >>canonical semantics (even after applying 11.6 permissions). I agree >>that it wouldn't be very common (it would mainly be noticeable on >>objects that are declared inside of the generic with the formal type, >>since outside objects by definition would have been copied when >>entering the generic), but can you make a case that such uses are pathological? >I didn't mean to claim it was pathological, but merely for the purposes >of RR's compiler in the near term, you could provide a warning. I also >wonder what your compiler would do when the formal private type has "with atomic" >on it. You still don't know whether the actual is a by-copy or a >by-reference type, so it seems like you would still have to have >multiple expansions, or have a run-time switch to do a copy if the >actual turns out to be a by-copy type. Perhaps you've missed (or forgotten) the baseline here. This issue occurs for *any* type that you can instantiate a formal private type with -- assuming you have code that can tell the difference between by-copy and by-reference parameter passing. For non-atomic actual types, I've always claimed that such a generic unit is pathological. Having code that can tell the difference that actually matters to the correctness of the generic unit would essentially mean that one would have a generic that can only successfully be instantiated (portably, at least) with a by-copy type (or a by-reference type). This not something that Ada allows contracting, so I view it as out-of-bounds. And even if one has such a generic, actually depending on the parameter passing mechanism is quite rare and fragile. Something like the following would work to detect this: generic type Priv is private; A_Val : in Priv; package Report_Passing is procedure Test (Obj : in out Priv); end Report_Passing; package body Report_Passing is procedure Real_Test (Obj1, Obj2 : in out Priv) is begin Obj1 := A_Val; if Obj1 = Obj2 then Put_Line ("By-reference parameter passing"); else Put_Line ("By-copy parameter passing"); end if; end Real_Test; procedure Test (Obj : in out Priv) is begin Real_Test (Obj, Obj); end Test; end Report_Passing; package I1 is new Report_Passing (Natural, 4); V1 : Natural := 12; I1.Test(V1); -- Should print "By-copy parameter passing", -- but will print "By-reference parameter passing" with Janus/Ada. As I noted above, I think caring about this is pathological, since many types that Report_Passing could be instantiated with would be allowed to report "By-reference parameter passing". If the correctness of the code actually depends on what this prints, then such as generic won't work with any non-elementary types, but it can be instantiated with such types and presumably will break. This (assuming this is pathological) has let me sleep at night without worrying about this topic. (I've known about it from the early 1990s without any idea of a viable solution -- unlike the majority of other issues with shared generics, the reallocate on assignment memory model, and other unique-ish features of Janus/Ada. Even if not fixed, I have extensive notes on how to fix them.) My concern here was that allowing atomic objects would somehow change this dynamic. The rules make it clear that removing operations is generally wrong. However, your comments and my own thought experiments suggest that the situation is not appreciably different for atomic objects. One reason for that is the possibility of by-reference atomic objects. Another reason is that the actual accesses that are made will certainly be atomic (as you noted, you wouldn't be able to convert such an object to a non-atomic type inside the generic). So it shouldn't matter to the vast majority of uses if there are a few less atomic accesses (with all of the other uncertainty in tasking, this is likely to be the least of the issues). Especially as the "right" thing would be done when calling into the generic from outside the instance. So I conclude that depending on the parameter passing mode is still pathological inside the generic, even if the types involved are atomic. That pretty much just reduces the problem to the known non-issue. I was trying to get to agree with me that a generic that has this issue is pathological, given that it won't work with types that it would be allowed to be instantiated with (and can't reasonably prevent being instantiated with). But I guess an agreement that it is the same as the long-standing problem (that so far as I know, no one has ever encountered) would be enough to let me sleep. :-) P.S. The real answer to your question is that Janus/Ada doesn't support any by-reference atomics: Atomic can only be applied to elementary types (at least, not until someone really needs more). So an Atomic formal private type (should such a thing ever get supported), necessarily would use by-copy passing. I'll have to keep this issue in mind before expanding any support, but that is a long ways away. ****************************************************************