Version 1.5 of ai12s/ai12-0317-1.txt

Unformatted version of ai12s/ai12-0317-1.txt version 1.5
Other versions for file ai12s/ai12-0317-1.txt

!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...

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

Questions? Ask the ACAA Technical Agent