!standard 3.10.2(16.1/5) 19-02-25 AI12-0317-1/05 !standard 4.3.2(5.1/5) !standard 4.3.2(5.2/5) !standard 4.3.2(5.3/5) !standard 4.3.2(5.4/5) !standard 4.3.2(5.5/5) !standard 4.4(9) !standard 4.5.9(6/5) !standard 6.2(10/5) !standard 7.5(2.1/5) !class Amendment 19-02-22 !status work item 19-02-22 !status received 19-02-21 !priority Low !difficulty Easy !subject Simplifying the rules for newly constructed objects !summary Introduce two new terms to avoid duplicate wording in a number of places. AI12-0236 exacerbates the problem of having to enumerate all cases where a construct like parenthesized expression or conditional expression passes through some property or some requirement to its operand(s). In addition, in two places we have nearly identical lists of kinds of expressions that represent newly constructed objects of a limited type. It would be nice to avoid this redundancy. !proposal We introduce the term "constituents" to represent the subexpressions that represent the "fundamental" computations of a given expression (or name), and we introduce the term "newly constructed" to represent expressions that create a new object out of thin air. !wording [Editor's note: 3.9.2(3) has too many special cases to be shifted over to using the "constituents" terminology.] Modify RM 3.10.2(16.1/5): In the above rules, the {constituents of a name or expression (see 4.4) are considered to be used in a given context if the enclosing name or expression is used in that context.} [operand of a view conversion, parenthesized expression or qualified_expression is considered to be used in a context if the view conversion, parenthesized expression or qualified_expression itself is used in that context. Similarly, a dependent_expression of a conditional_expression is considered to be used in a context if the conditional_expression itself is used in that context, and a body_expression of a declare_expression is considered to be used in a context if the declare_expression itself is used in that context.] Modify RM 4.3.2(5.1/5-5.5/5): If the type of the ancestor_part is limited and at least one component is needed in the record_component_association_list, then the ancestor_part shall not [be: ] {have a constituent expression (see 4.4) that is} a call to a function with an unconstrained result subtype{.}[; nor ... Add after RM 4.4(9): Every name or expression comprises one or more /constituent/ names or expressions, only one of which is evaluated as part of evaluating the name or expression (the /evaluated constituent/). The constituents are determined as follows, according to the form of the expression or name: * if the expression is a conditional_expression, the constituents of its dependent_expressions; * if the expression (or name) is a parenthesized expression, a qualified_expression, or a view conversion, the constituent(s) of its operand; * if the expression is a declare_expression, the constituent(s) of its body_expression; * otherwise, the expression (or name) itself. In certain contexts, we specify that a constituent shall (or shall not) be /newly constructed/. This means the constituent shall (or shall not) be an aggregate or a function_call; in either case, a raise_expression is permitted. Modify RM 4.5.9(6/5): A declare_item that is an object_renaming_declaration (see 8.5.1) shall not rename an object of a limited type {if any constituent of the object_name is a value conversion or is newly constructed (see 4.4).} [that is a function_call, aggregate, a parenthesized expression, qualified_expression, or type_conversion with an operand of one of these, a conditional_expression that has at least one dependent_expression that is one of these, or a declare_expression whose body_expression is one of these.] Modify RM 6.2(10/5): A parameter of a by-reference type is passed by reference, as is an explicitly aliased parameter of any type. Each value of a by-reference type has an associated object. [If For a parenthesized expression, qualified_expression, or view conversion, this object is the one associated with the operand.] For a value conversion, the associated object is the anonymous result object if such an object is created (see 4.6); otherwise it is the associated object of the operand. {In other cases, the object associated with the evaluated constituent of the name or expression (see 4.4) determines its associated object.} [For a conditional_expression, this object is the one associated with the evaluated dependent_expression. For a declare_expression, this object is the one associated with the body_expression.] Modify RM 7.5(2.1/5): In the following contexts, an expression of a limited type is [not] permitted {only if each of its constituents is /newly constructed/ (see 4.4):} [unless it is an aggregate, a function_call, a raise_expression, a parenthesized expression or qualified_expression whose operand is permitted by this rule, a conditional_expression all of whose dependent_expressions are permitted by this rule, or a declare_expression whose body_expression is permitted by this rule:] ... !discussion Introducing new terms means choosing among all the possible English words that communicate the appropriate notion, and that don't already have uses that conflict. We chose "constituent" and "evaluated constituent" after considering various other terms like "component" or "part" or "element" all of which have existing uses in Ada, as well as other terms which don't quite capture the inent. We chose "newly constructed" because it seems to capture the idea, without using the term "create" which already has a very technical meaning in Ada. We tried to replace the wording in 3.9.2 about statically and dynamically tagged, and tag-indeterminate to use the "constituents" terminology, but it just began to get out of hand. !ASIS No ASIS effect. !ACATS test ACATS tests might be needed for the minor changes in these rules: view conversions are allowed as newly constructed objects, and the 4.3.2 rule makes newly legal view conversions illegal in some cases. Otherwise, no separate ACATS tests should be needed, as the actual rules aren't changing. !appendix From: Tucker Taft Sent: Monday, February 11, 2019 2:31 AM There were several times we had debates about the best term to use for a particular concept. I remember talking about the term for those limited objects that can or cannot be renamed in a declare-expression, returned from a function, built in place, etc. Another term was Steve's "screened" or "supplementary" or "extended" or "superfluous" or ... Randy or anyone else, do you have notes on any other term(s) we wanted to give a (better) name to? **************************************************************** From: Tucker Taft Sent: Thursday, February 21, 2019 7:39 AM Below is a proposed addition to AI12-0236 (well, I suppose it really needs to be a "fix-up AI") which eliminates a redundant list of the kinds of expressions of a limited type that are allowed or disallowed based on whether they are newly constructed. [This is version /01 of the AI - Editor.] **************************************************************** From: Steve Baird Sent: Thursday, February 21, 2019 11:33 AM Do we need a new term (perhaps "potentially newly constructed") for this use in 4.5.9(6/5) where the treatment of conditional expressions needs to be different than in 7.5(2.1/5) (i.e., "at least one" vs. "all")? **************************************************************** From: Steve Baird Sent: Thursday, February 21, 2019 12:05 PM I think this change would result in incorrectly allowing some previously-disallowed type conversions, as in X : My_Immutably_Limited_Type renames My_Immutably_Limited_Type (Some_Function_Call); **************************************************************** From: Randy Bukardt Sent: Thursday, February 21, 2019 3:13 PM Correct. When requiring "newly constructed" items, we don't allow any type conversions, since they are either better written as a qualified expression, or a real pain to support in build-in-place. When not allowing this, we clearly don't want to allow type conversions of "newly constructed" items. Steve's other concern also matters. There really are three kinds of expressions: "newly constructed", "existing", and some sort of mix (where it is not known until runtime which it is). The mix case can't be allowed when either "existing" or "newly constructed" is required. So this isn't quite a binary choice that can be just negated by "not". I could see explicitly adding the two missing cases to the 4.5.9 wording (not showing changes 'cause it is a mess): A declare_item that is an object_renaming_declaration (see 8.5.1) shall not rename a newly constructed object of a limited type {(see 7.5), nor a type_conversion of such an object, nor a conditional_expression that has at least one dependent_expression that is such an object. although this isn't quite right because it doesn't support the recursion properly. I'm starting to think that the (almost) duplication isn't that bad. *************************************************************** From: Tucker Taft Sent: Thursday, February 21, 2019 3:44 PM I was thinking that conditional expressions of a limited type had to be all or none, no matter where they appeared. But I guess that isn't true. If it appears as an initialization expression or return expression, they all need to be newly constructed. But if it appears as a parameter, it can be some newly constructed, and some not. So yes, we need to deal with the case where some of the dependent_expressions of a conditional expression are newly constructed. I don't love the "potentially newly constructed," since this seems to be mixing up static and dynamic semantics. Perhaps we can define something like a "constituent" of a name, and require that no constituent of the object_name is allowed to be newly constructed if it is of a limited type. We can't just use a term like "subexpression" because we don't care anything about the parameters of a function call, nor about the subexpressions of the condition of an if_expression, etc. I'll send another attempt shortly, and try to address both issues Steve identified. **************************************************************** From: Tucker Taft Sent: Thursday, February 21, 2019 4:09 PM Can you remind me why we disallow renaming type conversions? They seem quite useful, especially if they are changing the type from specific to class-wide, or vice-versa. If we know the operand is not newly constructed, why is there any problem? **************************************************************** From: Tucker Taft Sent: Thursday, February 21, 2019 4:11 PM Never mind. We are only disallowing renaming of type conversions if their operand is newly constructed, and I agree we should continue to do so. **************************************************************** From: Tucker Taft Sent: Thursday, February 21, 2019 4:22 PM So here is another attempt. I have made a somewhat subtle change. I have distinguished view conversions and other conversions. View conversions are really the only "interesting" conversions from a renaming point of view, and we want to allow those, so long as the operand is renamable. Similarly, there seems no problem allowing a function to return a view conversion of a newly-constructed object, and this is important because a qualified expression cannot be used to change from specific to class-wide, or vice-versa (and all aggregates are of a specific type). In any case, here is the attempt: Define /constituents/ somewhere (referred to as RM X.X below): The /constituent(s)/ of an expression (or a name) are defined as follows, depending on the kind of the expression or name: * if the expression (or name) is a parenthesized expression, a qualified_expression, or a view conversion, the constituents of its operand; * if the expression is a conditional_expression, the constituents of its dependent_expressions; * if the expression is a declare_expression, the constituents of its body_expression; * otherwise, the expression (or name) itself. Modify RM 4.5.9(6/5): A declare_item that is an object_renaming_declaration (see 8.5.1) shall not rename an object of a limited type {if any constituent (see RM X.X) of the object_name is newly constructed (see 7.5)}[that is a function_call, aggregate, a parenthesized expression, qualified_expression, or type_conversion with an operand of one of these, a conditional_expression that has at least one dependent_expression that is one of these, or a declare_expression whose body_expression is one of these]. Modify RM 7.5(2.1/5): In the following contexts, an expression of a limited type is not permitted unless [it is] {each of its constituents (see RM X.X) is /newly constructed/, that is,} an aggregate, a function_call, {or} a raise_expression[, a parenthesized expression or qualified_expression whose operand is permitted by this rule, or a conditional_expression all of whose dependent_expressions are permitted by this rule, or a declare_expression whose body_expression is permitted by this rule]: ... **************************************************************** From: Randy Brukardt Sent: Thursday, February 21, 2019 4:47 PM > Can you remind me why we disallow renaming type conversions? I think you're confused. Type conversions of any sort are not allowed in newly constructed objects. The renames case needs to disallow type conversions of newly constructed objects, because we don't want to allow sort of newly constructed object, even ones that wouldn't be legal in a context where a newly constructed object is required. No one has asked to this point that renaming of type conversions of existing objects be banned. (The existing wording certainly does not do that.) Now that you mention it, however, I could see a case for doing so when there is a representation change -- such an object is more like a newly constructed object than it is like an existing one -- as the conversion is creating a new object. This is the reason that newly constructed objects don't allow type conversions (it would be a mess to implement build-in-place), and if we're trying to avoid renaming non-existing limited objects, a type conversion can fall into that category. The rules governing this are relatively new and found in 4.6(58.1-58.6). Essentially, a type conversion can be allowed if the type is a target type is a by-reference type and there is a type that is an ancestor of both the target type and the operand type (and, of course, the operation is not newly constructed). In other cases, new objects are possible. I don't suppose there is any real need to disallow elementary type conversions, either (this whole rule is aimed at composite types because of tasks and finalization). Since most limited types are by-reference and would qualify, most reasonable conversions could be allowed. The interesting question is whether many of the problematic conversions are legal anyway. The only ones I'd expect to be legal and troublesome are those between unrelated arrays of some limited type. Perhaps just requiring a common ancestor would be enough?? **************************************************************** From: Randy Brukardt Sent: Thursday, February 21, 2019 5:35 PM >So here is another attempt. This is mostly good. Three comments. 1) "RM X.X" isn't going to fly in an AI. You need to pick a place. I'd suggest 4.4 (which is the clause where expression is defined). 2) The change to allow view conversions is of course allowing more build-in-place expressions. This isn't necessarily bad, but of course there is an implementation cost associated. Hopefully not too much, don't want Bob to frazzle too much ;-). 3) The wording as you have it disallows renaming an expression that contains a raise_expression, even if it otherwise would be OK. It's hard to get too worked up about this, but that was allowed by the original wording. That is, I'm thinking of something like: Fooey renames Some_Limited_Type'(if X then Y else raise TBD_Error); where Y is an existing limited object. This happens since "raise_expression" is declared to be "newly constructed". (When in actual fact, it is more "whatever you want it to be"). 1B) It strikes me (especially if it is defined in 4.4) that the term "constituent" might be useful to reduce the clutter in a bunch of other existing rules. Because there are a lot of places where we allow parens and qualification (and probably many of them meant to allow the other things as well). If we have a nice term like this, it would be a pity not to use it more often than in just two rules. AI12-0236-1 has some candidate places. For instance, the set of rules 4.3.2(5.1/3-5.5/5) [ugh, there's a paragraph numbering error there] could be replaced by: If the type of the ancestor_part is limited and at least one component is needed in the record_component_association_list, then the ancestor_part shall not have a consistuent that is a call to a function with an unconstrained result subtype. That's not 100% the same (because it is now including view conversions), but it certainly seems like an improvement. (And I have to wonder if there is a bug in this rule associated with view conversions to unconstrained subtypes, it would seem to need to apply to those.) Perhaps the AI author could check other rules (6.2(10)?, 3.10.2(16.1/3)?, 3.9.2(3)?, perhaps others) to see if such a change would be sensible. **************************************************************** From: Steve Baird Sent: Friday, February 22, 2019 1:48 PM > 3) The wording as you have it disallows renaming an expression that > contains a raise_expression, even if it otherwise would be OK. Tuck - the way you factored out constituents seems elegant to me, but perhaps "newly constructed" should not include raise expressions; then the 7.5 rule would have to be something like "is not permitted unless each of its constituents is newly constructed or is a raise expression". This definition for "newly constructed" also seems more intuitive - does it really make sense to say that a raise expression is newly constructed? It might make sense to say that it does not reference a preexisting object, but that's not the terminology we are using here. **************************************************************** From: Tucker Taft Sent: Friday, February 22, 2019 2:14 PM Below is an attempt to address Randy's comments in this AI12-0236 "fix-up" AI. [This is version /02 of the AI - Editor.] **************************************************************** From: Tucker Taft Sent: Friday, February 22, 2019 2:18 PM >> 3) The wording as you have it disallows renaming an expression that >> contains a raise_expression, even if it otherwise would be OK. > > Tuck - the way you factored out constituents seems elegant to me, but > perhaps "newly constructed" should not include raise expressions; then > the 7.5 rule would have to be something like "is not permitted unless each > of its constituents is newly constructed or is a raise expression". > > This definition for "newly constructed" also seems more intuitive - > does it really make sense to say that a raise expression is newly > constructed? It might make sense to say that it does not reference a > preexisting object, but that's not the terminology we are using here. Sorry, I didn't see this e-mail before I sent my update. As far as renaming "raise expressions" of a limited type, it is hard to get too excited about them. Clearly a raise expression is at least conceptually creating a new object, rather than copying an existing object. Do you think it is important to allow a renaming of them in a declare expression? **************************************************************** From: Tucker Taft Sent: Friday, February 22, 2019 2:29 PM ... > !problem > > AI12-0236 exacerbates the problem of having to enumerate all cases > where a construct like parenthesized expression or conditional > expression passes through some property or some requirement to its > operand(s). In two places, we have nearly identical lists of kinds of > expressions that represent newly constructed objects of a limited type. These are two separate problems, so probably should have been two separate paragraphs. ... > Modify RM 3.9.2(16.1/5): Oops, I meant RM 3.10.2(16.1/5). Heart of darkness strikes again! > > In the above rules, the {constituents of a name or expression (see > 4.4) are considered to be used in a context if the enclosing name or > expression is used in a given context.} ... **************************************************************** From: Steve Baird Sent: Friday, February 22, 2019 2:43 PM > Clearly a raise expression is at least conceptually creating a new object, rather than copying an existing object. I disagree. It is certainly clear that it is not copying/referencing an existing object, but that (to me) does not mean that it is creating a new object. It is an oversimplification to assume that those are the only two options. When we are saying that creation of a new object is required, then we want to include raise expressions in with one category. When we are saying that creation of a new object is forbidden, then we want to include raise expressions in with the other category. If folks agree that getting this exactly right isn't worth the trouble, then I can live with that. **************************************************************** From: Tucker Taft Sent: Friday, February 22, 2019 2:56 PM > It is certainly clear that it is not copying/referencing an existing > object, but that (to me) does not mean that it is creating a new > object. It is an oversimplification to assume that those are the only two > options. > > When we are saying that creation of a new object is required, then we > want to include raise expressions in with one category. > > When we are saying that creation of a new object is forbidden, then we > want to include raise expressions in with the other category. I suppose, though I think it is simpler to only have two categories, where we think of a raise expression as a call on a function that always raises an exception, and whose result type morphs into whatever type is expected. > If folks agree that getting this exactly right isn't worth the > trouble, then I can live with that. I would be in that camp, since having to always worry about a third category just to accommodate the presence of raise-expressions seems like unnecessary complexity. **************************************************************** From: Randy Brukardt Sent: Friday, February 22, 2019 3:09 PM > Below is an attempt to address Randy's comments in this > AI12-0236 "fix-up" AI. Looks good, except: ... > Modify RM 6.2(10/5): > > A parameter of a by-reference type is passed by reference, as is an > explicitly aliased parameter of any type. Each value of a by-reference > type has an associated object. {The evaluated constituent of a name or > expression (see 4.4) determines the associated object.} [If For a > parenthesized expression, qualified_expression, or view conversion, > this object is the one associated with the operand. For a value > conversion, the associated object is the anonymous result object if > such an object is created (see 4.6); otherwise it is the associated > object of the operand. For a conditional_expression, this object is > the one associated with the evaluated dependent_expression. For a > declare_expression, this object is the one associated with the > body_expression.] You lost the value conversion case from this wording. (Consistuents don't include value conversions.) > Modify RM 7.5(2.1/5): > > In the following contexts, an expression of a limited type is not > permitted unless [it is] {each of its constituents (see 4.4) is > /newly constructed/, that is,} an aggregate, a function_call, {or} a > raise_expression[, a parenthesized expression or qualified_expression > whose operand is permitted by this rule, a conditional_expression > all of whose dependent_expressions are permitted by this rule, or a > declare_expression whose body_expression is permitted by this rule]: > ... I tend to agree with Steve that raise_expression ought to be handled specially here. When I thought of the problem yesterday, it didn't bother me much, but when I wrote an example using a TBD exception, it started to look possible: (declare Foo renames Lim_Type'(if B then Obj else raise TBD_Error); begin ... I write a lot of code with -- *** TBD comments, and it seems that using a TBD exception instead would be safer. But that doesn't work if there are contexts that don't allow raise_expressions just because we were lazy. **************************************************************** From: Tucker Taft Sent: Friday, February 22, 2019 3:26 PM ... > You lost the value conversion case from this wording. (Consistuents > don't include value conversions.) Oops, right you are. I noticed the special case when I started, but forgot it by the time I finished... ;-) ... > I write a lot of code with -- *** TBD comments, and it seems that > using a TBD exception instead would be safer. But that doesn't work if > there are contexts that don't allow raise_expressions just because we were > lazy. Hmmm... I am not sure it is laziness. I see it as trying to avoid creating a lot of special cases for raise-expressions, and instead adopting a uniform model, namely they are equivalent to a call on a function that raises an exception. Such things are usable almost everywhere. Do we really need to create this third case just for renaming of limited objects in declare expressions? That really seems like some sort of priority inversion. ;-) **************************************************************** From: Tucker Taft Sent: Friday, February 22, 2019 3:39 PM Below is a version incorporating a few more fixes. [This is version /03 of the AI - Editor.] **************************************************************** From: Randy Brukardt Sent: Friday, February 22, 2019 4:10 PM ... > > I write a lot of code with -- *** TBD comments, and it seems that > > using a TBD exception instead would be safer. But that doesn't work > > if there are contexts that don't allow raise_expressions just > > because we were lazy. > > Hmmm... I am not sure it is laziness. I see it as trying to avoid > creating a lot of special cases for raise-expressions, and instead > adopting a uniform model, namely they are equivalent to a call on a > function that raises an exception. > Such things are usable almost everywhere. Do we really need to create > this third case just for renaming of limited objects in declare > expressions? That really seems like some sort of priority inversion. > ;-) Some wise person (might have been you) once noted that if there has to be a complexity in either the user model or in the RM wording, we should put the complexity in the RM everytime. We shouldn't be afraid of extra RM wording if it will make the user experience better. Because we want the user model to be as seamless as possible. (This is also related to your "bump under the carpet" analogy.) If the example I showed is plausible (and I think it is), someone will eventually trip over it. Is simplifying the RM wording worth leaving a "bump" in the user model?? (declare Foo renames Lim_Type'(if B then Obj else raise TBD_Error); begin ... I doubt whomever trips over this is going to be interested in your argument that raise TBD_Error is equivalent to a function that raises TBD_Error. :-) We clearly want as little illegal here as possible (subject to the goal of preventing temporary objects containing tasks being declared here). **************************************************************** From: Tucker Taft Sent: Friday, February 22, 2019 4:24 PM > ... > We clearly want as little illegal here as possible (subject to the > goal of preventing temporary objects containing tasks being declared here). OK, enough said. Hence, here is yet another version. [This is version /04 of the AI - Editor.] **************************************************************** From: Tucker Taft Sent: Saturday, February 23, 2019 10:22 AM After more thought, I decided putting the definition of "newly constructed" in section 7.5 was not great, especially as I found new places where it might be used which had nothing to do with limited types. Also, my handling of raise-expressions was not ideal, in that they had to be handled as special cases when we started talking about newly-constructed objects. I felt a more general permission would be useful. So, here is one more version (yeah, I know, Randy already uploaded all the AIs). So this is an update to what is now identified as AI12-0317-1... [This is version /05 of the AI - Editor.] **************************************************************** From: Randy Brukardt Sent: Saturday, February 23, 2019 8:20 PM > After more thought, I decided putting the definition of > "newly constructed" in section 7.5 was not great, especially > as I found new places where it might be used which had > nothing to do with limited types. The AI doesn't use it in any such places, or even tell us in the !discussion where they might be. > Also, my handling of > raise-expressions was not ideal, in that they had to be > handled as special cases when we started talking about > newly-constructed objects. I felt a more general permission > would be useful. So, here is one more version (yeah, I know, > Randy already uploaded all the AIs). Sigh. I suppose it doesn't pay to insist on discussing the previous version, and if I don't post it half of the group won't find it, so I've posted an update. > So this is an update to what is now identified as AI12-0317-1... ****************************************************************