Version 1.10 of ai05s/ai05-0142-4.txt

Unformatted version of ai05s/ai05-0142-4.txt version 1.10
Other versions for file ai05s/ai05-0142-4.txt

!standard 3.10(9/2)          10-06-11 AI05-0142-4/07
!standard 3.10.2(13.1/2)
!standard 3.10.2(19/2)
!standard 6.1(15/2)
!standard 6.1(23)
!standard 6.2(3)
!standard 6.2(10)
!standard 6.3.1(16/2)
!standard 6.4.1(6)
!standard 6.4.1(15)
!standard 7.6.1(13/3)
!standard C.6(12)
!class Amendment 09-05-17
!status ARG Approved 7-0-3 10-06-19
!status work item 09-05-17
!status received 09-05-17
!priority Medium
!difficulty Medium
!subject Explicitly aliased parameters
!summary
(See proposal.)
!problem
Modifying a portion of a larger opaque object (such as a container) is not well-supported in Ada. The Ada.Containers packages provide a procedure Replace_Element for this purpose. But this procedure requires copying the element (potentially in both directions). That could be very expensive if the element is large.
The Ada.Containers packages also provide an procedure Update_Element for this purpose; this provides a writable object as a parameter to a subprogram passed into the procedure. This procedure avoids the need to copy the element, but it is hardly convenient. The extra syntax needed obscures the real meaning of the program.
An option that was rejected for the Ada.Containers packages was to return an access that designates the element type. That is problematic as it is difficult to control the accessibility and lifetime of the returned access. If the element is removed from the container, the returned access could become dangling; continued use of the access would make the program erroneous. Moreover, the accessibility of the returned object (and thus what could be done with it) would depend on the actual implementation of the container. Bounded containers would typically only return access values with a very short lifetime, while unbounded containers would typically return access values with a much longer lifetime. Converting from an unbounded to bounded form could thus introduce new runtime errors - a serious maintenance hazard.
!proposal
Add explicitly aliased parameters, which allow 'Access of parts to be returned as access discriminants and anonymous access returns.
This proposal, along with others, are used in the containers updates found in AI05-0212-1.
!wording
Modify the first sentence of 3.10(9/2):
A view of an object is defined to be aliased if it is defined by an object_declaration{, parameter_specification, } or component_definition with the reserved word aliased, or by renaming an aliased view.
Add after 3.10.2(13.1/2):
The accessibility level of an explicitly aliased (see 6.1) formal parameter in a function body is determined by the point of call (which is the same level that the return object ultimately will have).
[That level is defined by 3.10.2(10.1/2).].
[The intent is that this level is such that the checks defined in 6.5 for return statements will always succeed (or fail) without any need for any runtime checks and associated overhead. (See the Language Design Principles below.) This depends on the proposed solution for AI-51; the return object then will have the correct level. - RLB]
Add after 3.10.2(19/2): [This is a modification of a rule from AI05-0051-1, and replaces that rule.]
* Inside a return statement that applies to a function F, when determining whether the accessibility level of an explicitly aliased parameter of F is statically deeper than the level of the return object of F, the level of the return object is presumed to be the same as that of the level of the explicitly aliased parameter; for statically comparing with the level of other entities, the level of the return object of F is presumed to be the same as that of the master that elaborated the function body of F.
Replace 6.1(15/2) by:
parameter_specification ::= defining_identifier_list : [aliased] mode [null_exclusion] subtype_mark [:= default_expression] | defining_identifier_list : access_definition [:= default_expression]
Add after 6.1(23): [Static Semantics]
An explicitly aliased parameter is a formal parameter whose parameter_specification includes the reserved word aliased.
Modify 6.2(3):
A type is a by-copy type if it is an elementary type, or if it is a descendant of a private type whose full type is a by-copy type. A parameter of a by-copy type {that is not explicitly aliased} is passed by copy.
Modify 6.2(10):
A parameter of a by-reference type is passed by reference{, as is an explicitly aliased parameter of any type}. Each value...
Modify 6.3.1(16/2):
Two profiles are mode conformant if they are type-conformant, [and] corresponding parameters have identical modes, {both or neither are explicitly aliased parameters}, and, for access parameters or access result types, the designated subtypes statically match, or the designated profiles are subtype conformant.
Add in 6.4.1 of the AARM:
Language Design Principles For explictly aliased parameters of functions, we will ensure at the call site that a part of the parameter can be returned as part of the function result without creating a dangling pointer. We do this with accessibility checks at the call site that all actual objects of explicitly aliased parameters live as long as the function result; then we can allow them to be returned as access discriminants or anonymous access results, as those have the master of the function result as well.
Add after 6.4.1(6): [Legality Rules]
If the formal parameter is an explicitly aliased parameter, the type of the actual parameter shall be tagged or the actual parameter shall be an aliased view of an object. Further, if the formal parameter subtype F is untagged:
* the subtype F shall statically match the nominal subtype of the actual object; or
* the subtype F shall be unconstrained, discriminated in its full view, and unconstrained in any partial view.
AARM Ramification: Tagged objects (and tagged aggregates for in parameters) do not need to be aliased. This matches the behavior of unaliased formal parameters of tagged types, which allow 'Access to be taken of the formal parameter regardless of the form of the actual parameter.
AARM Reason: We need the subtype check on untagged actual parameters so that the requirements of 'Access are not lost. 'Access makes its checks against the nominal subtype of its prefix, and parameter passing can change that subtype. But we don't want this parameter passing to change the objects that would be allowed as the prefix of 'Access. This is particularly important for arrays, where we don't want to require any additional implementation burden.
In a function call, the accessibility level of the actual object for each explicitly aliased parameter shall not be statically deeper than accessibility level of the master of the function result.
[Editor's note: We could have made this rule apply to all calls, since it can only fail for functions that initialize allocators, but that doesn't seem to add anything interesting. Note that we don't change the lifetime of anonymous objects or the accessibility of aliased parameters for procedures (since there is no return object to change the accessibility to), so it would be somewhat weird to have this apply to procedures as well.]
AARM Discussion: Since explicitly aliased parameters are either tagged or required to be objects, there is always an object (possibly anonymous) to talk about. This is discussing the static accessibility level of the actual object; it does not depend on any runtime information (for instance when the actual object is a formal parameter to another call).
AARM Ramification: This accessibility check (and its dynamic cousin as well) can only fail if the function call is used to directly initialize a built-in-place object with a master different than that enclosing the call. The only place all of those conditions exist is in the initializer of an allocator; in all other cases this check will always pass.
Add a new Dynamic Semantics rule after 6.4.1(15): (this would be a new, outer level, bullet)
* In a function call, for each explicitly aliased parameter, a
check is made that the accessibility level of the master of the actual object is not deeper than that of the master of the function result.
AARM Ramification: If the actual object to a call C is a formal parameter of some function call F, no dynamic check against the master of the actual parameter of F is necessary. Any case which could fail the dynamic check is already statically illegal (either at the call site of F, or at the call site C). This is important, as it would require nasty distributed overhead to accurately know the dynamic accessibility of a formal parameter (all tagged and explicitly aliased parameters would have to carry accessibility levels).
Modify 7.6.1(13/3): [This wording was modified by AI05-0066-1]
The master of an object is the master enclosing its creation whose accessibility level (see 3.10.2) is equal to that of the object, except in the case of an anonymous object representing the result of an aggregate or function call. {If such an anonymous object is part of the actual parameter expression for an explicitly aliased parameter of a function call, the master of the object is the innermost master enclosing the evaluation of the aggregate or function call, excluding the aggregate or function call itself. Otherwise, the}[The] master of such an anonymous object is the innermost master enclosing the evaluation of the aggregate or function call, which may be the aggregate or function call itself.
AARM Reason: (Add after the existing ones) The special case for explicitly aliased parameters of functions is needed for the same reason, as access discriminants of the returned object may designate one of these parameters. In that case, we want to lengthen the lifetime of the anonymous objects as long as the possible lifetime of the result.
We don't do a similar change for other kinds of calls, because the extended lifetime of the parameters adds no value, but could consistute a storage leak. For instance, such an anonymous object created by a procedure call in the elaboration part of a package body would have to live until the end of the program, even though it could not be used after the procedure returns (other than via Unchecked_Access). End AARM reason
AARM Ramification: Note that the master given to anonymous objects in explicitly aliased parameters of functions is not necessarily as long as the master of the object being initialized (if the funciton call is used to initialize an allocator, for instance). In that case, the accessibility check on explicitly aliased parameters will necessarily fail if any such anonymous objects exist. This is necessary to avoid requiring the objects to live as long as the access type or having the implementation complexity of an implicit coextension.
Modify the first sentence of C.6(12):
If an atomic object is passed as a parameter, then the [type of the] formal parameter shall either [be atomic]{have an atomic type} or allow pass by copy [(that is, not be a nonatomic by-reference type)].
AARM Reason: A formal parameter allows pass by copy if it is not aliased and it is of a type that allows pass by copy (that is, is not a by-reference type).
[Editor's note: We're deleting the parenthetical remark, as it is no longer true by itself.]
!discussion
The idea is that the accessibility level of explicitly aliased parameters of functions is defined such that returning a part (or all) of the parameter as an anonymous access result or as an access discriminant will always succeed. That means no checks (and associated failures) need to be done within the function; we make any needed checks at the call site instead.
We include the aliased property of parameters in mode conformance, so that the need (or not) to make accessibility checks is always known at compile-time. This means that it cannot be hidden by renames or generic formal parameters, and most importantly, does not change for dispatching routines. It would be a disaster for a dispatching call to call a function that has aliased parameters without making necessary accessibility checks on those parameters; that would be possible if "aliased" was not part of mode conformance.
---
We don't want to treat all parameters of functions like explicitly aliased parameters for compatibility and wording reasons.
We don't want to have to define accessibility checks on values, and we proved that trying to tie this property to particular types causes maintenance hazards (see the earlier alternative AI05-0142-3 to see why).
In addition, the accessibility checks needed could cause some functions used to initialize an allocator to become illegal or raise Program_Error. While that is relatively rare, it still would be hard to work around.
Finally, changing the master of all anonymous objects in parameters of functions would make function parameters live a long time in some cases (such as when the result is renamed or part of an allocator). That could be especially nasty if the parameter is an object that contains tasks that otherwise would have been terminated or releases locks when finalized. This would be the worst sort of incompatibility, where the program still works but does something different.
---
The call site checks can fail only in obscure cases, as the vast majority of objects that could be passed to a function call will outlive the function result. However, if the function result is built-in-place, the result could be made library-level simply by including a call in an allocator for a library level access type. In that case, we do not want to be returning an access designating a local object. The check is normally a static one, and imposes overhead only in rare cases (such as passing a dereference of an anonymous access parameter).
For example:
package Test is type Rec (A : access Integer) is null record; -- Build-in-place type.
type A_Rec is access Rec;
Root : A_Rec;
function Constructor (Param : aliased in out Integer) return Rec is begin return (A => Param'access); end Constructor;
procedure Do_It is My_Value : aliased Integer := 10; begin Root := new Rec'(Constructor (My_Value)); -- Illegal. end Do_It;
begin Do_It; if Root.A = 10 then -- A doesn't exist anymore. ... end if; end Test;
Since Rec is built-in-place, its access discriminants have the master of the access type A_Rec. That lives longer than the parameter My_Value, which is used as the access discriminant. Thus we have to make something illegal somewhere. We chose to make an accessibility check at the call site, as this do not usually require the overhead of dynamic checks.
---
We briefly considered and rejected allowing explicitly aliased function results. Such returns would need to work as-if they are returned by reference.
We could allow aliased results when a function call is used directly as the actual for an explicitly aliased parameter. We already do allow that a function that returns a tagged type (since these are always considered aliased), and most likely we could allow something similar for untagged types.
But other uses would require the result to be built in place to get the effect of a reference return. We don't want to require build-in-place semantics for non-limited types, as that is not safe when assigning into existing objects, and we don't want to add to the implementation burden.
---
The legality rule that requires static matching for untagged subtypes is required in order to prevent problems with the parameter passing changing the nominal constraint of the object. If 'Access is taken later, it might be legal when otherwise taking 'Access of that object directly would have been illegal.
For instance:
type vector is array (positive range <>) of Natural;
Actual : aliased vector (1 .. 4); -- Nominal subtype is constrained
function Has_Four_Elements (x : aliased in vector) return Boolean is type Ref is access constant vector; Ptr : Ref; begin if X'First > 1 then Ptr := Actual'access; -- (1) return Ptr.all'Length = 4; else Ptr := X'access; -- (2) return Ptr.all'Length = 4; end if; end Has_Four_Elements;
Has_Four : constant Boolean := Has_Four_Elements (Actual);
(1) is illegal because the subtypes don't statically match. However, (2) is legal because the nominal subtype of X does statically match the designated type of Ref, Vector. We don't want incentives to pass something as a parameter so it can be used where it would not be able to be used directly.
Note that this check in the array case is primarily about implementations being able to continue to require a contiguous representation of array dope. If the compiler does not assume that contiguous representation, it would be simple to implement this case by creating the needed dope at the point of the function call. Unlike the normal case of 'Access (where the needed lifetime of the dope is unclear, making a likely storage leak, even if non-continguous representations are supported), here the dope only needs to exist as long as the function result does - the same lifetime as any other temporary created as part of the evaluation of the actual of an explicitly aliased parameter.
We also need this check in order to prevent mismatches in scalar subtypes; for instance, we surely do not want to create an access-to-Integer that designates an aliased Natural object.
This rule is annoying as it is reasonable (for a container, for instance) to never actually use 'Access on the entire passed object in the body. An access to some component may be referenced instead. But we think that for typical containers (implemented as private types, either without discriminants or with unconstrained discriminants), a client would never run into this rule. That's because of the exception for unconstrained discriminated types and because a private type without discriminants could not have a constraint that didn't match. The check is most likely to fail for arrays and scalar types, but in these cases it it much more likely that the entire object will be returned.
It has been noted that we could leverage the existing 'Constrained flag to make part of this check at runtime. However, this really does not help very much. We would still have to use static matching if the subtype of the explicitly aliased parameter is constrained. And most other cases are already legal. We could change the 6.4.1(6) wording to:
Further, if the formal parameter subtype F is untagged:
* the subtype F shall statically match the nominal subtype of the actual object; or
* the subtype F shall be unconstrained and discriminated.
But then we would need to add after to 3.10.2(29):
If the designated type D is untagged, discriminated in its full view, and constrained in any partial view, a check is made X'Constrained is False; Program_Error is raised if this check fails.
AARM Notes: This check can only fail if the view is a formal parameter, in all other cases static matching will ensure that it is successful.
This doesn't seem to save much wording nor allow many additional cases.
We could get more bang for our buck by defining another attribute that would work on arrays:
A'Nominally_Constrained
For an explicitly aliased formal parameter F of an array type, returns True if the nominal subtype of the actual object is constrained, and returns False otherwise. For other objects of an array type, returns True if the nominal subtype of the object is constrained, and returns False otherwise.
Then the 6.4.1(6) wording could be changed to: Further, if the formal parameter subtype F is untagged:
* the subtype F shall statically match the nominal subtype of the actual object; or
* the subtype F shall be unconstrained and either discriminated or an array subtype.
We would need to add after to 3.10.2(29) (in addition to the wording above):
If the designated type D is an array type and unconstrained, a check is made X'Nominally_Constrained is False; Program_Error is raised if this check fails.
This would have some additional runtime overhead, but would allow more flexibility for array parameter passing (any sort of array object could be passed to an explicitly aliased unconstrained array parameter).
Note that we could make changes like these in the future; they would be compatible with the current rules (as they would only allow more). So we adopt the more restrictive rule now and we can liberalize it in the future if needed.
---
Given that we had to make a check of 3.10.2(27-27.2/2) to avoid problems, one might wonder why we don't need to make a matching check of 3.10.2(26). Here, however, the existing rules of erroneousness prevent trouble. To see that, consider:
type Rec (B : Boolean := False) is record case B is when True => C1 : aliased Natural; when False => null; end case; end record;
Obj : Rec := (True, C1 => 10);
function A_Func (P : aliased in Natural) return access Natural is begin Obj := (False); -- (1) return P'access; -- (2) end A_Func;
if A_Func.all(Obj.C1) = 10 then null; end if;
The assignment to Obj at (1) changes the discriminant and causes the component Rec.C1 to cease to exist. That's bad because that component has been passed (by reference) as an aliased parameter, and we then use that parameter after the component has gone away.
Luckily, 3.7.2(4) makes execution of A_Func erroneous. Thus we don't need additional checks for this case. (And it would be weird to have them, given that regular parameter passing does not try to prevent this problem.)
!examples
See AI05-0212-1 for how this feature is defined and used in the Containers libraries, including examples of use.
!ACATS test
ACATS tests would be needed for this feature.
!appendix

From: Tucker Taft
Sent: Sunday, May 17, 2009  11:39 AM

You ask whether "aliased" goes before or after the mode.
My instinct would say before the mode, since "aliased" goes before "constant" in
an aliased constant, and a mode is most like the word "constant" in its effect.
I also think it reads a bit better.

If it does come before, in general, then I would *require* some explicit mode
specifier, rather than making "in" the default.  I.e.:

     [[aliased] mode] [null_exclusion] ...

From a higher level point of view, since "aliased" pretty dramatically affects
the mode (e.g. by changing by-copy to be by-reference) it seems like it belongs
in front of the mode.  That is, you aren't just passing "in" an aliased object,
but you are actually using an "aliased" parameter passing mechanism.

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

From: Bob Duff
Sent: Sunday, May 17, 2009  12:35 PM

> You ask whether "aliased" goes before or after the mode.
> My instinct would say before the mode, since "aliased" goes before
> "constant" in an aliased constant, and a mode is most like the word
> "constant" in its effect.  I also think it reads a bit better.

I agree.

> If it does come before, in general, then I would *require* some
> explicit mode specifier, rather than making "in"
> the default.

Why?  What's wrong with "X : aliased T;"?  Why do you prefer to force it to be
"X : aliased in T;"?

>...I.e.:
>
>      [[aliased] mode] [null_exclusion] ...

I don't think this syntax accomplishes your goal, given:

16    mode ::= [in] | in out | out

>  From a higher level point of view, since "aliased" pretty
> dramatically affects the mode (e.g. by changing by-copy to be
> by-reference) it seems like it belongs in front of the mode.  That is,
> you aren't just passing "in" an aliased object, but you are actually
> using an "aliased" parameter passing mechanism.

I agree.

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

From: Tucker Taft
Sent: Sunday, May 17, 2009  12:58 PM

> Why?  What's wrong with "X : aliased T;"?  Why do you prefer to force
> it to be "X : aliased in T;"?

It just seems that defaults are only useful if it is self-evident what they
should be.  For aliased formal parameters, a default of "in" seems less
appropriate.  Also, somehow the default in the "middle" (between "aliased" and
the type) seemed a bit weird.  Normally defaults show up at the end, or
conceivably at the front, but in the middle seemed less satisfying.

>
>> ...I.e.:
>>
>>      [[aliased] mode] [null_exclusion] ...
>
> I don't think this syntax accomplishes your goal, given:
>
> 16    mode ::= [in] | in out | out

Good point.  I guess "mode" should not be marked as optional, then.  Perhaps
"aliased" could be part of the mode:

     mode ::= [[aliased] in] | [aliased] in out | [aliased] out

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

From: Tucker Taft
Sent: Sunday, May 17, 2009  5:01 PM

> It just seems that defaults are only useful if it is self-evident what
> they should be.  For aliased formal parameters, a default of "in"
> seems less appropriate.  Also, somehow the default in the "middle"
> (between "aliased" and the type) seemed a bit weird.  Normally
> defaults show up at the end, or conceivably at the front, but in the
> middle seemed less satisfying.

OK, thanks for explaining your point of view.  I don't really agree, I mean,
everybody knows "in" is the default, right?  Self-evidently.  ;-)  And if
somebody thinks "aliased" means "aliased in out", they will be disabused of that
notion pretty quickly by the compiler.

But anyway, I don't feel strongly.

> >> ...I.e.:
> >>
> >>      [[aliased] mode] [null_exclusion] ...
> >
> > I don't think this syntax accomplishes your goal, given:
> >
> > 16    mode ::= [in] | in out | out
>
> Good point.  I guess "mode" should not be marked as optional, then.
> Perhaps  "aliased" could be part of the mode:
>
>      mode ::= [[aliased] in] | [aliased] in out | [aliased] out

I don't much like that, because "aliased" is not really part of the mode; this
takes us further from the analogy with "aliased constant".

If we go this way, I'd prefer a syntax rule written in English, "If you say
'aliased', then 'in' must be explicit" or RM-ese to that effect.  Or else
redesign the syntax rules:

    mode ::= in | in out | out

Then change "mode" to "[mode]" in various existing places, and then your
syntax:

   [[aliased] mode] [null_exclusion] ...

works.

Might require a few wording tweaks -- we need to make sure the default mode is
'in' in existing places.

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

From: Tucker Taft
Sent: Sunday, May 17, 2009  5:12 PM

I don't feel strongly either, though I do feel more strongly that "aliased"
should come before the mode indicator (if any).

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

From: Bob Duff
Sent: Sunday, May 17, 2009  7:14 PM

Yes, I agree with that.

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

From: Randy Brukardt
Sent: Tuesday, May 19, 2009  3:15 PM

> I don't feel strongly either, though I do feel more strongly that
> "aliased" should come before the mode indicator (if any).

OK, I'll rewrite it that way. I think we should leave the issue about
the default for the full ARG, as it doesn't look like a consensus is
apparent. (And I don't really want a syntax argument to complicate
matters.)

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

From: Bob Duff
Sent: Sunday, May 17, 2009  12:32 PM

> Attached find my first attempt at "aliased parameters". It looks
> pretty good, at least until Steve gets a hold of it. Probably some of
> the accessibility wording is convoluted. [This was version /01 - ED.]

Looks very promising.  Good job!

Mostly just editorial comments, below.

> The Ada.Containers packages also provide an procedure Update_Element
> for = this purpose; this provides a writable object as a parameter to
> a subprogram = passed into the procedure. This procedure avoids the
> need to copy the element, = but it is hardly convenient to define a
> procedure for every component of the = element that needs changing.

I'd erase the part starting "to define..." -- doesn't seem to add much.

>... The extra syntax needed obscures the real meaning =  of the
>program.
>
> An option that was rejected for the Ada.Containers packages was to =
> return an access to the element type. However, this is problematic as
> it is = difficult to control the accessibility and lifetime of the
> returned access.

Not clear what "this" refers to -- the rejection?

>... If the =
> element is
> removed from the container, the returned access could become dangling;
>=  continued  use of the access would make the program erroneous.
>Moreover, the =  accessibility  of the returned object (and thus what
>could be done with it) would =  depend on the  actual implementation of
>the container. Bounded containers would =  typically only  return
>access values with a very short lifetime, while unbounded =  containers
>would typically return access values with a much longer lifetime. =
>Converting  from an unbounded to bounded form could thus introduce new
>runtime =  errors - a  serious maintenance hazard.
>
> !proposal
>
> Add aliased parameters, which allow 'Access of parts to be returned as
> = access discriminants and anonymous access returns.
>
> !wording
>
> Modify the first sentence of 3.10(9/2):
>
> A view of an object is defined to be *aliased* if it is defined by an
> = object_declaration{, parameter_specification, } or
> component_definition with the reserved = word *aliased*, or by
> renaming an aliased view.
>
> Add somewhere in 3.10.2(6-16): [Q: Where??]

Shrug.

> The accessibility level of a formal aliased parameter in a function
> body

                               aliased formal parameter

> is that that the return object will have after a return statement =

"that that" only made sense to me on the third reading. ;-) How about "is the
level the ..."?

> completes
> normally.
>
> [That level is defined by 3.10.2(10.1/2).].
>
> [The intent is that this level means that the checks defined in 6.5
> for = return statements will always succeed (or fail) without any need
> for any = runtime garbage. (See the Language Design Principles below.)
> But this depends on =
  "garbage"?

> what
> we end up doing for AI-51; this works best with the fully dynamic =
> version of that AI. If we use Bob's version, then we will have to make
> a hole in the =

Bob's version is based on the premise that these things are pretty-much useless,
and therefore the rules should simplify implementations above all else.  But if
you've found a way to make them useful, then Bob would probably withdraw his
suggestion.

> rules to
> allow this scenario. One way to do that would be use immutably limited
> = results only, in that case, the other rules given above would be
> changed = similarly.
> Another option would be a syntax marker on the return that we want a =
> shorter lifetime for the result. - RLB]
>
>
> Replace 6.1(15/2) by:
>
> parameter_specification ::=3D
>    defining_identifier_list : mode [aliased] [null_exclusion] =
> subtype_mark [:=3D default_expression]  | defining_identifier_list :
> access_definition [:=3D = default_expression]
>
> [Q: Does [aliased] go before or after the mode? I thought it worked =
> better after the mode, as then the types are more like the types in an
> object_declaration. But it = probably doesn't matter.]

I think it matters, and I think "aliased" belongs before the mode.
My reason: Because "then the types are more like the types in an
object_declaration."  ;-)  See Tuck's explanation.

> Add after 6.1(23): [Static Semantics]
>
> An *aliased parameter* is a formal parameter that includes the
> reserved =
                                               ^^^^whose declaration
> word *aliased*.

I suspect we might get in trouble if we use the term "aliased parameter".
Better spell it out, "aliased formal parameter", I think.

Actually, it's worse: tagged formal parameters are aliased, so this term is
fundamentally confusing.

> Modify 6.3.1(16/2):
>
> Two profiles are mode conformant if they are type-conformant, [and] =
> corresponding parameters have identical modes, {both or neither are
> aliased parameters}, and, for =
How about:               both or neither include the reserved word 'aliased',
and, for

> access parameters or
> access result types, the designated subtypes statically match, or the
> = designated profiles are subtype conformant.
>
> Add in 6.4.1 of the AARM:
>
> Language Design Principles
> For aliased parameters of functions, we will ensure at the call site =
> that a part of it can be returned as part of the function result
> without creating a = dangling pointer.
> We do this with accessibility checks at the call site that all actual
> = objects of aliased parameters live as long as the function result;
> then we can = allow them to be returned as access discriminants or
> anonymous access results, as = those have the master of the function
> result as well.
>
> Add after 6.4.1(6): [Legality Rules]

I don't know if it works everywhere, but if you replace the above definition of
"aliased parameter" with "A formal parameter is 'explicitly aliased' if its
declaration includes the reserved word *aliased*.", then you could say:

> If the formal parameter is an aliased parameter, the type of the
> actual =
                                explicitly aliased
> parameter shall be
> tagged or the actual parameter shall be an aliased view of an object.
>
> AARM Ramification: Tagged objects (and tagged aggregates for *in* =
> parameters) do not need
> to be aliased. This matches the behavior of unaliased formal
> parameters, = which allow 'Access to be taken of any actual parameter.

I don't understand that last phrase.

> In a function call, the accessibility level of the actual object for
> each aliased parameter shall not be statically deeper than
> accessibility level of the master of the function result.
>
> [We could make this rule apply to all calls, since it can only fail
> for = functions that initialize allocators, but that doesn't seem to
> add anything = interesting. Note that we don't change the lifetime of
> anonymous objects or the accessibility of = aliased parameters for
> procedures, so it would be somewhat weird to have this apply to =
> procedures as well.]

Could you remind us why procedures are different?

> AARM Discussion: Since aliased parameters are either tagged or
> required = to be objects, there is always an object (possibly
> anonymous) to talk about. This is = discussing the static
> accessibility level of the actual object; it does not depend = on any
> runtime information (for instance when the actual object is a formal =
> parameter to another call).
>
> AARM Ramification: This accessibility check (and its dynamic cousin as
> =
> well) can only fail
> if the function call is used to directly initialize a built-in-place =
> object with a master different than that enclosing the call. The only
> place all of those = conditions exist is in the initializer of an
> allocator; in all other cases this check will = always pass.
>
> Add a new Dynamic Semantics after 6.4.1(15): (this would be a new,
> outer = level, bullet)
>
> In a function call, for each aliased parameter, a check is made that
> the master of the accessibility level of the actual =
Heh? "master of level"?

> object is
> the same as or includes that of master of the function result.
>
> AARM To Be Honest: We're talking about the "nominal" level of the
> actual = object.
> If the actual object is a formal parameter of some function call F, we
> = do not intend to require dynamic checks that depend on the master of
> the actual parameters to F, as that would cause nasty distributed
> overhead (all = tagged and aliased parameters would have to carry
> accessibility levels).
>
>
> Modify 7.6.1(13/3): [This wording was modified by AI05-0066-1]
>
> The master of an object is the master enclosing its creation whose =
> accessibility level (see 3.10.2) is equal to that of the object,
> except in the case of an = anonymous object representing the result of
> an aggregate or function call. {If such an = anonymous object is part
> of the actual parameter expression for an aliased parameter of a =
> function call, the master of the object is the innermost master
> enclosing the = evaluation of the aggregate or function call,
> excluding the aggregate or function call itself. = Otherwise,
> the}[The] master of such an anonymous object is the innermost master
> enclosing the = evaluation of the aggregate or function call, which
> may be the aggregate or function = call itself.
>
> AARM Reason: (Add after the existing ones) The special case for
> aliased parameters of functions is needed for the = same reason, as
> access discriminants of the returned object may designate one of =
> these parameters.
> In that case, we want to lengthen the lifetime of the anonymous
> objects = as long as the possible lifetime of the result.
>
> AARM Ramification:
> Note that the master given to anonymous objects in aliased parameters
> of = functions is not necessarily as long as the master of the object
> being initialized = (if the funciton call is used to initialize an
> allocator, for instance). In that case, = the accessibility check on
> aliased parameters will necessarily fail if any such anonymous =
> objects exist.
> This is necessary to avoid requiring the objects to live as long as
> the = access type or the implementation complexity of an implicit
> coextension.
>
>
> Add the following to each Ada.Containers package immediately after
> Update_Element (with suitable substitutions for the names of types) =
> [note that the names of the type, discriminant, and function are TBD].
>
>     type Accessor_Type (Element : not null access Element_Type) is
>         tagged limited private;

I'm inclined to make the discriminant name very short, maybe just "E", because
Accessor_Type conceptually _is_ a pointer, so the extra ".Element" is just
noise.  The only reason it exists is for some internal checking inside the
containers package.

> Accessor_Type needs finalization.
>
> A default-initialized object of type Accessor_Type propagates =
> Program_Error.
>
> AARM Reason: It is expected that Accessor_Type will be a controlled
> type, for which finalization will have some action to terminate the =
> tampering check for the associated container. If the object is created
> by default, however, there is no associated container. Since this is
> useless, and = supporting this case would take extra work, we define
> it to raise an exception.
>
>
>     function Accessor (Container : in out aliased Vector; Position :
> in =
> Cursor)
>        return Accessor_Type;
>
> If Position equals No_Element, then Constraint_Error is propagated; if
> = Position does not designate an element in Container, then
> Program_Error is = propagated.
> Otherwise, Accessor returns an object whose discriminant is an access
> to =

"discriminant designates"

> the
> element designated by Position. Program_Error is propagated if any =
> operation tampers with the elements of Container while the object
> returned by = Accessor exists and has not been finalized.
>
> The element designated by Position is not an empty element after =
> successful completion of this operation. [This is only needed for the
> Vectors case. =
> - ED]
>
> Add the following to Ada.Containers.Vectors and its relatives.
>
>     function Accessor (Container : in out aliased Vector; Index : in =
> Index_Type)
>        return Accessor_Type;
>
> If Index is not in the range First_Index (Container) .. Last_Index =
> (Container), then Constraint_Error is propagated. Otherwise,
>
> Accessor returns an object whose discriminant is an access to the =

"discriminant designates"

> element at
> position Index. Program_Error is propagated if any operation tampers =
> with the elements of Container while the object returned by Accessor
> exists and = has not been finalized.
>
> The element designated by Position is not an empty element after =
> successful completion of this operation.
>
> Add the following to the Erroneous Execution section of each container
> =
> package:
>
> Execution is erroneous if the vector associated with the result of a =
> call to Accessor is finalized before the result object returned by
> Accessor is finalized.
>
> AARM Reason: Each object of Accessor_Type probably contains some =
> reference to the originating container. If that container is
> prematurely finalized = (only possible via Unchecked_Deallocation, as
> accessibility checks prevent = passing a container to Accessor that
> will not live as long as the result), the finalization of the object
> of Accessor_Type will try to access a = non-existent object. This is a
> normal case of a dangling pointer created by Unchecked_Deallocation;
> we have to explicitly mention it here as the = pointer in question is
> not visible in the specification of the type. (This is the = same
> reason we have to say this for invalid cursors.)
>
> [Q: Should we add the read-only accessors?

Probably.

>.. They would look like:
>
>     type RO_Accessor_Type (Element : not null access constant =
> Element_Type) is
>        tagged limited private;
>
>     function RO_Accessor (Position : in aliased Cursor) return =
> RO_Accessor_Type;
>
>     function RO_Accessor (Container : in aliased Vector; Index : in =
> Index_Type)
>        return RO_Accessor_Type;
>
> The names of all of these functions need work.]
>
> !discussion
>
> The idea is that the accessibility level of aliased parameters of =
> functions is defined such that returning a part (or all) of the
> parameter as an anonymous = access result or as an access discriminant
> will always succeed. That means no checks (and = associated failures)
> need to be done within the function; we make any needed checks at the
> call = site instead.
>
> The call site checks can fail only in obscure cases, as the vast =
> majority of objects that could be passed to a function call will
> outlive the function result. However, if = the function result is
> built-in-place, the result could be made library-level simply by =
> including a call in an allocator for a library level access type. In
> that case, we do not want to be = returning an access to a local
> object. The check is normally a static one, and imposes overhead only
> in = rare cases (such as passing a dereference of an anonymous access
> parameter).
>
> We include the aliased property of parameters in mode conformance, so
> = that the need (or not) to make accessibility checks is always known
> at compile-time. This means = that it cannot be hidden by renames or
> generic formal parameters, and most importantly, = does not change for
> dispatching routines. It would be a disaster for a dispatching call to
> = call a function that has aliased parameters without making necessary
> accessibility checks on = those parameters; that would be possible if
> "aliased" was not part of mode conformance.
>
> ---
>
> Note that the accessibility of the returned object from a call to =
> Accessor will prevent converting the returned access discriminant to
> any type that lives longer than the = returned object. That means that
> we can assume that the discriminant value cannot be accessed after =
> the object disappears.
>
> It is possible to use Unchecked_Deallocation to destroy the returned =
> Accessor_Type object prematurely, while continuing to use the returned
> access. The = accessibility check on the container argument to the
> Accessor function means that the returned access can't = live longer
> than the container, but the use of Unchecked_Deallocation would allow
> tampering = on the container. This could look like:
>
>    declare
>       type AT is access MV.Accessor_Type;
>       procedure Free is new Ada.Unchecked_Deallocation =
> (MV.Accessor_Type, AT);
>
>       PAT : AT :=3D new MV.Accessor_Type'(Vect.Accessor (1))
>
>       Element : access Element_Type :=3D PAT.Element; -- OK.
>
>    begin
>       Free (PAT); -- Tampering checking ends here!
>
>       Vect.Delete (1); -- No check here.
>       ... Element ... -- Oops, gone.
>    end;
>
> Obviously, this is highly unlikely to happen by accident. This is more
> = like shooting oneself in the foot by attaching a laser target to
> your foot and then firing a = laser-seeking missile. It doesn't seem
> worth worrying about someone who wants to go to these = lengths to
> avoid a tampering check - they're just as likely to use
> Unchecked_Conversion or = .all'Unchecked_Access or something else
> nasty.

Agreed that we needn't worry about this case.

> ---
>
> We don't want to treat all parameters of functions like aliased =
> parameters for compatibility and wording reasons.
>
> We don't want to have to define accessibility checks on values, and we
> = proved that trying to tie this property to particular types causes
> maintenance hazards (see = the earlier alternative AI05-0142-3 to see
> why).
>
> In addition, the accessibility checks needed could cause some
> functions = used to initialize an allocator to become illegal or raise
> Program_Error. While that is = relatively rare, it still would be hard
> to work around.
>
> Finally, changing the master of all anonymous objects in parameters of
> = functions would make function parameters live a long time in some
> cases (such as when the = result is renamed or part of an allocator).
> That could be especially nasty if the parameter = is an object that
> contains tasks that otherwise would have been terminated or releases =
> locks when finalized.
> This would be the worst sort of incompatibility, where the program
> still = works but does something different.
>
> ---
>
> We need to be able to define the period of existence of the return =
> object of the accessor as a time when tampering with elements is
> prohibited. If we didn't do = this, we would be opening the door to
> all manner of problems (up to and including =
> erroneousness) if
> an element is added or deleted to the container.
>
> To see how this could happen, consider the following example:
>
> Assume that Ada.Containers.Vectors has an accessor function defined as
> =
> follows:
>
> function Accessor (C : in out aliased Vector; I : Index_Type) return =
> access Element_Type;
>
> Now consider the following program fragments:
>
>   package Data is
>     type Node (D : Boolean :=3D True) is record
>           case D is
>               when True =3D>
>                   CT : Natural;
>               when False =3D>
>                   CF : Float;
>           end case;
>     end record;
>     subtype True_Node  is Node (D =3D> True);
>     subtype False_Node is Node (D =3D> False);
>
>     package Node_Vector is new Ada.Containers.Vectors (Element_Type
> =3D> = Node,
>            Index_Type =3D> Positive);
>
>     Node_List : Node_Vector.Vector;
>   end Data;
>
>   with Data;
>   package Far_Away is
>     procedure Some_Complex_Operation (...);
>   end Far_Away;
>
>   package body Far_Away is
>     procedure Some_Complex_Operation (...) is
>         Some_Index : Positive;
>     begin
>         ...
>         Some_Index :=3D <lengthy-computation-resulting-in-value-of-1>;

:= Ident_Int(1);

>         ...
>         Data.Node_List.Delete (Some_Index);
>         ...
>     end Some_Complex_Operation;
>   end Far_Away;

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

From: Randy Brukardt
Sent: Tuesday, May 19, 2009  4:15 PM

...
> > The Ada.Containers packages also provide an procedure Update_Element
> > for = this purpose; this provides a writable object as a parameter
> > to a subprogram = passed into the procedure. This procedure avoids
> > the need to copy the element, = but it is hardly convenient to
> > define a procedure for every component of the = element that needs changing.
>
> I'd erase the part starting "to define..." -- doesn't seem to add
> much.

OK.

> >... The extra syntax needed obscures the real meaning =  of the
> >program.
> >
> > An option that was rejected for the Ada.Containers packages was to =
> > return an access to the element type. However, this is problematic
> > as it is = difficult to control the accessibility and lifetime of
> > the returned access.
>
> Not clear what "this" refers to -- the rejection?

"returning an access to the element type from Ada.Containers". Which is why I
used "this", way too long otherwise. I suppose I could shorten it to "returning
an access"

However, returning an access is problematic as it is difficult to control the
accessibility and lifetime of the returned access.

But maybe the real problem is the "However", since there is no reverse sense
here. Maybe just "That is problematic...".

...
> > Add somewhere in 3.10.2(6-16): [Q: Where??]
>
> Shrug.

That's helpful. ;-)

> > The accessibility level of a formal aliased parameter in a function
> > body
>
>                                aliased formal parameter
>
> > is that that the return object will have after a return statement =
>
> "that that" only made sense to me on the third reading. ;-) How about
> "is the level the ..."?

Sounds good.

> > completes
> > normally.
> >
> > [That level is defined by 3.10.2(10.1/2).].
> >
> > [The intent is that this level means that the checks defined in 6.5
> > for = return statements will always succeed (or fail)
> without any need
> > for any = runtime garbage. (See the Language Design
> Principles below.)
> > But this depends on =
>   "garbage"?

The problem with late-at-night AI drafting. I was thinking that the code that
executes runtime checks does not progress the application to its final result,
so it is just garbage as far as the final result is concerned. Should just say
"checks", or maybe "checks and associated overhead" (since it is passing the
runtime levels that is the expensive part).

> > what
> > we end up doing for AI-51; this works best with the fully dynamic =
> > version of that AI. If we use Bob's version, then we will have to
> > make a hole in the =
>
> Bob's version is based on the premise that these things are
> pretty-much useless, and therefore the rules should simplify
> implementations above all else.  But if you've found a way to make
> them useful, then Bob would probably withdraw his suggestion.

Well, it seems to me that in this case, we don't need the full dynamic overhead,
so we probably could do better -- but the problem is we would need different
rules for this case rather than normal ones.

We could even tie it to the presence on explicitly aliased parameters, except
that would have the effect of making other kinds of uses of aliased parameters
hard.

...
> > [Q: Does [aliased] go before or after the mode? I thought it worked
> > = better after the mode, as then the types are more like the types
> > in an object_declaration. But it = probably doesn't matter.]
>
> I think it matters, and I think "aliased" belongs before the mode.
> My reason: Because "then the types are more like the types in an
> object_declaration."  ;-)  See Tuck's explanation.

OK, I've changed this based on the opinion of you and Tucker.

> > Add after 6.1(23): [Static Semantics]
> >
> > An *aliased parameter* is a formal parameter that includes the
> > reserved =
>                                                ^^^^whose declaration
> > word *aliased*.

Humm; formal parameters aren't declarations, so I don't think this wording
works. Besides, the wording for "aliased" entity in 3.10(9) doesn't say anything
about declarations. We could say "formal parameter whose parameter_specification
includes...".

> I suspect we might get in trouble if we use the term "aliased parameter".
> Better spell it out, "aliased formal parameter", I think.
>
> Actually, it's worse: tagged formal parameters are aliased, so this
> term is fundamentally confusing.

I think you are right, and changed it to explicitly aliased parameter
everywhere. Not sure if we need the "parameter", but "explicitly aliased" is
rather broad, it woud be weird if the object declaration
   A : aliased B;
is not "explicitly aliased".

We can revisit this later if its too weird.

...
> > Add after 6.4.1(6): [Legality Rules]
>
> I don't know if it works everywhere, but if you replace the above
> definition of "aliased parameter" with "A formal parameter is
> 'explicitly aliased' if its declaration includes the reserved word
> *aliased*.", then you could say:
>
> > If the formal parameter is an aliased parameter, the type of the
> > actual =
>                                 explicitly aliased
> > parameter shall be
> > tagged or the actual parameter shall be an aliased view of an object.

See above.


> > AARM Ramification: Tagged objects (and tagged aggregates for *in* =
> > parameters) do not need
> > to be aliased. This matches the behavior of unaliased formal
> > parameters, = which allow 'Access to be taken of any actual
> parameter.
>
> I don't understand that last phrase.

"This matches the behavior of unaliased formal parameters {of tagged types},
which allow 'Access to be taken of {the formal parameter regardless of the form
of the}[any] actual parameter."

> > In a function call, the accessibility level of the actual object for
> > each aliased parameter shall not be statically deeper than
> > accessibility level of the master of the function result.
> >
> > [We could make this rule apply to all calls, since it can only fail
> > for = functions that initialize allocators, but that doesn't seem to
> > add anything = interesting. Note that we don't change the lifetime
> > of anonymous objects or the accessibility of = aliased parameters
> > for procedures, so it would be somewhat weird to have this apply to
> > = procedures as well.]
>
> Could you remind us why procedures are different?

Because the extended lifetime of anonymous objects has no benefit for
procedures, but could consistute a storage leak if the objects are large. For
instance, such an anonymous object created by a procedure call in the
elaboration part of a package body would have to live until the end of the
program, even though it could not be used after the procedure returns (other
than via Unchecked_Access). This should be mentioned in the AARM notes under
7.6.1(13/3) [and now is].


> > Add a new Dynamic Semantics after 6.4.1(15): (this would be a new,
> > outer = level, bullet)
> >
> > In a function call, for each aliased parameter, a check is
> made that
> > the master of the accessibility level of the actual =
> Heh? "master of level"?

Just testing your reading comprehension. ;-) I did warn you that some of the
accessibility wording was confused.

...
> > Add the following to each Ada.Containers package immediately after
> > Update_Element (with suitable substitutions for the names of types)
> > = [note that the names of the type, discriminant, and
> function are TBD].
> >
> >     type Accessor_Type (Element : not null access Element_Type) is
> >         tagged limited private;
>
> I'm inclined to make the discriminant name very short, maybe just "E",
> because Accessor_Type conceptually _is_ a pointer, so the extra
> ".Element"
> is just noise.  The only reason it exists is for some internal
> checking inside the containers package.

Well, it's noise no matter what it is named: E, Element, or Gigantic_Muggywump.
Ada usually uses full words, so I used the longer (not joke) name. But I don't
care much, naming always leads to such passion that I don't expect to finalize
it now.

> > Otherwise, Accessor returns an object whose discriminant is
> an access
> > to =
>
> "discriminant designates"

That can't be right, 3.10(1) says that "designates" is something that an access
value does to an object. I don't mind using a better verb here, but one that has
a defined meaning (and surely could be confused here, given that we're talking
about access types) won't do.

We could say "an access that designates the element" (that's better than "to").

...
> >         Some_Index :=3D
> <lengthy-computation-resulting-in-value-of-1>;
>
> := Ident_Int(1);

Only long-time ACATS users are likely to be familiar with the optimization
blocking Ident_Int function. It would take longer to explain it to everyone else
than to just say it as I did.

(We could confuse them even more by calling is Report.Ident_Int. :-)

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

From: Tucker Taft
Sent: Sunday, May 17, 2009  1:00 PM

I think somewhere you need to state that aliased parameters are always passed by
reference, whether elementary or composite.

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

From: Steve Baird
Sent: Monday, May 18, 2009  11:43 AM

This looks generally very good.

I'd like to see more examples (e.g. illustrating the success/failure cases that
the various static and dynamic rules are intended to distinguish).

I think the same subtype matching checks that are associated with a use of
X'Access need to be performed when X is passed as an actual parameter.

Example #1 -

     type immutably_limited is  ... ; -- contains tasks, etc.

     type vector is array (positive range <>) of immutably_limited;

     function Has_Four_Elements (x : aliased in vector) return Boolean is
         type Ref is access constant vector;
         Ptr : ref := X'access;
     begin
         return Ptr.all'Length = 4;
     end Has_Four_Elements;

     actual : aliased vector (1 .. 4); -- nominal subtype is constrained

     has_Four : constant Boolean := Has_Four_Elements (Actual);

This example should be illegal.

Consider an implementation which uses a contiguous-dope-vector representation to
implement access types where the designated subtype is an unconstrained array
subtype.

Because the nominal subtype of Actual is constrained, no such contiguous dope
vector would be allocated for Actual. Has_Four_Elements would, however, expect
that dope vector to be there.

Example #2-

    I am skeptical about aliased scalar parameters to begin with
    (although I understand the generic contract issues that would
    result if we wanted to disallow them).

    There are similar problems with say, an "aliased in out natural"
    parameter being passed an actual parameter whose subtype is
    either Integer or Positive.

Are there issues with the constrainedness of a discriminated actual/formal
parameter pair?  I need to think about this one.

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

From: Tucker Taft
Sent: Monday, May 18, 2009  12:16 PM

I think the rules for 'Access solve most of the problems.  That is, think of it
as though it said "access Natural" rather than "aliased Natural." Ditto for
access-to-discriminated record, which already have well-defined, safe rules.

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

From: Steve Baird
Sent: Monday, May 18, 2009  2:01 PM

I agree that this is the right way to think of it.

One might respond to this by saying "if you really meant an anonymous access
parameter, then why didn't you say so? Why are we even talking about a parameter
of the designated type?".

One of the advantages of the "aliased parameter" approach is that it doesn't
allow allocators as actual parameters, which means that we don't have to work
out the finalization/accessibility/storage_pool rules for such a beast.

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

From: Tucker Taft
Sent: Monday, May 18, 2009  3:23 PM

It presumably *does* allow tagged aggregates (for aliased "in"), so we aren't
completely off the hook, since they can have coextensions.

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

From: Steve Baird
Sent: Monday, May 18, 2009  3:38 PM

You're right.

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

From: Bob Duff
Sent: Tuesday, May 19, 2009  5:35 PM

> > > Otherwise, Accessor returns an object whose discriminant is an access
> > > to =
> >
> > "discriminant designates"
>
> That can't be right, 3.10(1) says that "designates" is something that
> an access value does to an object. I don't mind using a better verb
> here, but one that has a defined meaning (and surely could be confused
> here, given that we're talking about access types) won't do.
>
> We could say "an access that designates the element" (that's better
> than "to").

I don't understand your objection.  It seems like "designates" is exactly
what you want, here.  The discriminant is an access value, and it designates
some object (a component of a vector or whatever).

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

From: Randy Brukardt
Sent: Tuesday, May 19, 2009  6:03 PM

Your suggestion "discriminant designates an access to ..." does not make sense.

Best I can do is: "discriminant is an access that designates ..." does make sense.

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

From: Bob Duff
Sent: Tuesday, May 19, 2009  6:03 PM

> Your suggestion "discriminant designates an access to ..." does not
> make sense.

I agree it makes no sense, but that's not my suggestion.  ;-) Sorry for being unclear.
My suggestion is to replace this:

    Otherwise, Accessor returns an object whose discriminant is an access to
    the element designated by Position.

with this:

    Otherwise, Accessor returns an object whose discriminant designates
    the element designated by Position.

The following also works:

    Otherwise, Accessor returns an object whose discriminant is an access value
    that designates the element designated by Position.

but I think it's unnecessarily verbose -- we know it's an access value, because it's
declared to be of an access type, just a few paragraphs earlier.

I don't think we ever use the phrase "an access to" in the RM.
(If we called the silly things "pointers", like the rest of the world does, wording
would be easier!)

Note: similar wording appears elsewhere in the AI.

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

From: Randy Brukardt
Sent: Wednesday, May 20, 2009  12:24 PM

> but I think it's unnecessarily verbose -- we know it's an access
> value, because it's declared to be of an access type, just a few
> paragraphs earlier.

OK, but I disagree. I really want to emphasize that an access (not some sort of
value) is being returned.

> I don't think we ever use the phrase "an access to" in the RM.
> (If we called the silly things "pointers", like the rest of the world
> does, wording would be easier!)

I do realize that, which is why I did replace the "to" with "that designates".

> Note: similar wording appears elsewhere in the AI.

I only found 3 instances of "access to". The first one is in the !problem section,
which is informal but probably would be better off written properly anyway. The
second one is talking about taking 'Access, not the same thing at all. The third
one is in the !discussion, again informal text, but I changed it anyway.

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

From: Tucker Taft
Sent: Thursday, May 21, 2009  5:27 AM

I had a couple of "waking up" thoughts, as Jimmy Carter used to call them:

   1) Since formal parameters of a discriminated type
      have a 'Constrained attribute, we can accommodate
      Randy's desire to relax the matching rules somewhat
      for explicitly aliased formal parameters of a
      discriminated type.

      For 'Access, since there is no 'Constrained attribute
      for heap objects, we have to have tighter matching
      rules since you have to be able to decide whether
      a heap object is constrained from the declaration
      of the access type (now that we allow unconstrained
      objects in the heap).

      In fact, I don't think this will make much difference,
      but at least the wording will be simpler, since we
      don't need to talk about full views and partial views
      and all of that.  Essentially the idea is that the
      tighter matching requirements for 'Access will be
      enforced if and when someone takes 'Access of an
      explicitly aliased formal, by making use of the
      'Constrained attribute.

      Note that we could add a similar attribute for aliased
      formals of an array type, though it would indicate whether
      the actual's nominal *subtype* was constrained, because
      of course all array *objects* are constrained.

[Editor's note: The other thought pertains to AI05-0135-1 and is filed in that AI.]

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

From: Randy Brukardt
Sent: Friday, May 22, 2009  12:45 PM

>    1) Since formal parameters of a discriminated type
>       have a 'Constrained attribute, we can accommodate
>       Randy's desire to relax the matching rules somewhat
>       for explicitly aliased formal parameters of a
>       discriminated type.
>
>       For 'Access, since there is no 'Constrained attribute
>       for heap objects, we have to have tighter matching
>       rules since you have to be able to decide whether
>       a heap object is constrained from the declaration
>       of the access type (now that we allow unconstrained
>       objects in the heap).

That's exactly what I was talking about when I said that I thought a runtime check might
be better. I had forgotten that we already require the necessary mechanism for discriminated
types.

>       In fact, I don't think this will make much difference,
>       but at least the wording will be simpler, since we
>       don't need to talk about full views and partial views
>       and all of that.  Essentially the idea is that the
>       tighter matching requirements for 'Access will be
>       enforced if and when someone takes 'Access of an
>       explicitly aliased formal, by making use of the
>       'Constrained attribute.

Well, it would allow something like:

    type Rec (D : Natural := 100) is ...

    Cobj : aliased Rec(10);

    function Accessor (Param : aliased in out Rec; ...) return ...

    := Accessor (Cobj);

as long as the body of Accessor does not use 'Access of the entire parameter into an
access-to-Rec.

Actually, this seems pretty important, because the above is very similar to the form
of the bounded containers. The actual objects have bounds, but of course the subprograms
take unconstrained objects. (The difference is that the bounded containers, being tagged
objects, don't allow defaulted discriminants. But surely I can imagine someone building
an untagged container.)

    Acc : access Rec := Cobj'Access; -- Illegal.

(At least I hope the above is illegal, 'cause it would allow changing the discriminant
of a constrained object!! Acc.all := (D => 20, ...) surely is legal.)

>       Note that we could add a similar attribute for aliased
>       formals of an array type, though it would indicate whether
>       the actual's nominal *subtype* was constrained, because
>       of course all array *objects* are constrained.

Well, you've almost convinced me that that isn't important. I'd rather not add any runtime
overhead to the parameter passing if we can avoid it.

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

From: Tucker Taft
Sent: Friday, May 22, 2009  2:12 PM

> Well, it would allow something like:
>
>     type Rec (D : Natural := 100) is ...
>
>     Cobj : aliased Rec(10);
>
>     function Accessor (Param : aliased in out Rec; ...) return ...
>
>     := Accessor (Cobj);
>
> as long as the body of Accessor does not use 'Access of the entire
> parameter into an access-to-Rec.

This would be legal even if we used the rules for 'Access, though I realize you would
need convincing.  The relevant paragraph is 3.10.2(27.2).  But I agree it is simpler
to explain and understand if we simply fall back on the availability of the 'Constrained
attribute.

> Actually, this seems pretty important, because the above is very
> similar to the form of the bounded containers. The actual objects have
> bounds, but of course the subprograms take unconstrained objects. (The
> difference is that the bounded containers, being tagged objects, don't
> allow defaulted discriminants. But surely I can imagine someone
> building an untagged
> container.)

The only time "access Rec" doesn't allow "Cobj'Access"
is if there is a partial view of Rec that is constrained (that is, the partial view is
simply "type Rec is private;", and the discriminants are only visible in the private part).
We don't allow Cobj'Access in that case because the assumption is that all designated
objects are unconstrained (to avoid privacy breaking).  For a container either the
discriminants would be visible, or clients could never declare constrained objects,
so you would never bump into the problem.

But as I said, if by falling back on 'Constrained we can simplify the description and
make one R.R. happier, that's fine with me!

>     Acc : access Rec := Cobj'Access; -- Illegal.
>
> (At least I hope the above is illegal, 'cause it would allow changing
> the discriminant of a constrained object!! Acc.all := (D => 20, ...)
> surely is
> legal.)

No it's not legal to change the discriminants through an access value, since
in most cases, heap objects are constrained. In 2005 we added an exception for
types whose partial view has no visible discriminant part -- those are presumed
*always* unconstrained in the heap, but of course only code that has visibility
on the full type would even notice.  At least that is what 3.10.2(27.2) is
*supposed* to be accomplishing. Apparently at least in your case, it accomplished
total obfuscation.  Perhaps a note (if only in the AARM) might be useful on this paragraph.

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

From: Randy Brukardt
Sent: Friday, May 22, 2009  11:45 PM

...
> > Well, it would allow something like:
> >
> >     type Rec (D : Natural := 100) is ...
> >
> >     Cobj : aliased Rec(10);
> >
> >     function Accessor (Param : aliased in out Rec; ...) return ...
> >
> >     := Accessor (Cobj);
> >
> > as long as the body of Accessor does not use 'Access of the entire
> > parameter into an access-to-Rec.
>
> This would be legal even if we used the rules for 'Access, though I
> realize you would need convincing.  The relevant paragraph is
> 3.10.2(27.2).  But I agree it is simpler to explain and understand if
> we simply fall back on the availability of the 'Constrained attribute.

I obviously have had a brain meltdown, must be the sudden onset of summer (we seem
to have skipped spring altogether). But now I wonder if 'Constrained would be helpful.

> > Actually, this seems pretty important, because the above is very
> > similar to the form of the bounded containers. The actual objects
> > have bounds, but of course the subprograms take unconstrained
> > objects. (The difference is that the bounded containers, being
> > tagged objects, don't allow defaulted discriminants. But surely I
> > can imagine someone building an untagged container.)
>
> The only time "access Rec" doesn't allow "Cobj'Access"
> is if there is a partial view of Rec that is constrained (that is, the
> partial view is simply "type Rec is private;", and the discriminants
> are only visible in the private part).
> We don't allow Cobj'Access in that case because the assumption is that
> all designated objects are unconstrained (to avoid privacy breaking).
> For a container either the discriminants would be visible, or clients
> could never declare constrained objects, so you would never bump into
> the problem.

Humm. In that case, 'Constrained is definitely *not* what we want. We want static matching
in all cases unless the parameter subtype is discriminated and unconstrained (unconstrained
is the usual case, of course). And we don't want any checks at all unless the partial view
exception exists. While 'Constrained will be true for a passed-in constrained object. So we
would still need all of the "partial view" wording on the runtime check at the point of 'Access,
because that is the only case when we would want to check that. (We surely don't want to
check it for access-to-"ordinary"-unconstrained, because there is no reason to allow *less* here).

I don't quite buy the last sentence, although I agree that *clients* couldn't bump into the
problem. But if that is the case, we might as well just stick with the original legality rules
on the call, since it would be fairly hard for them to fail in normal usage, so long as people
avoid messing with aliased array parameters or aliased scalar parameters. And you'd convinced
me that we really don't care about those latter cases.

(That will come back to bite us, I suspect. BTW, you said that you couldn't remember the last
time someone complained about these static matching rules. As it turns out, it was Adam in
Ada-Comment waaaay back in March. Of 2009. Like I said, it is a *very* common complaint.)

I'll add some "alternative" discussion about potential runtime checks to avoid the static checks.

> >     Acc : access Rec := Cobj'Access; -- Illegal.
> >
> > (At least I hope the above is illegal, 'cause it would allow
> > changing the discriminant of a constrained object!! Acc.all := (D =>
> > 20, ...) surely is legal.)
>
> No it's not legal to change the discriminants through an access value,
> since in most cases, heap objects are constrained.

Well, you have convinced me that the above is legal. <PEDANT> And it is *legal* to try to
change the discriminants of a designated object: but it must raise Constraint_Error.</PENDANT>

> In 2005 we added an exception for types whose partial view has no
> visible discriminant part -- those are presumed
> *always* unconstrained in the heap, but of course only code that has
> visibility on the full type would even notice.  At least that is what
> 3.10.2(27.2) is *supposed* to be accomplishing.
> Apparently at least in your case, it accomplished total obfuscation.
> Perhaps a note (if only in the AARM) might be useful on this
> paragraph.

I think there is such a note in the vicinity. But of course I thought I remembered how this
all works, rather than actually looking it up. Which doesn't always work out. :-) And what I
did look up I read in the paper RM, so I didn't see the AARM notes.

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

From: Christoph Grein
Sent: Monday, July  6, 2009  4:44 AM

I have a reference counting safe pointers package:

generic

  type Object is limited private;

package Safe_Pointers.On_Limited_Types is

  type Safe_Pointer is private;

  function Value (Pointer: Safe_Pointer) return Object;
  -- further operations

private

  -- not shown

end Safe_Pointers.On_Limited_Types;

This was valid in Ada 95: Value was a return-by-reference function and returned
a constant view. Ada 2005 removed the return-by-reference-types, so I have to
replace Value by:

  function Value (Pointer: Safe_Pointer) return access Object;

which ruins the safety I had before with safe pointers.

If I understand the AI correctly, it would allow to define

  type Accessor_Type (Element: not null access Object) is tagged limited private;

  function Accessor (Pointer: aliased in out Safe_Pointer) return Accessor_Type;

and I could write

  X: Accessor_Type := Accessor (My_Pointer);

where X.Element.all is a constant view of the object denoted by My_Pointer (a
variable view if Limited is removed everywhere above), which would restore the
safety of my safe pointers.

How would I define the complete type and the body of Value?

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

From: Randy Brukardt
Sent: Monday, July 6, 2009  1:13 PM

...
> where X.Element.all is a constant view of the object denoted by
> My_Pointer (a variable view if Limited is removed everywhere above),
> which would restore the safety of my safe pointers.

X.Element.all is always a variable view; if you wanted a constant view you would
need to add "constant" to the discriminant declaration.

> How would I define the complete type and the body of Value?

Well, I assume you mean the body of "Accessor" in your example (you'd probably
call that Value). It depends of course on what's in your private part, since you
didn't give that I can only guess.

Assuming that the type Safe_Pointer is implemented as follows:

    type Unsafe_Pointer is access all Object;

    type Safe_Pointer is record
        Ptr : Unsafe_Pointer;
        Refs : Natural := 0;
        ...
    end record;

You can make your function Value very simple:

    function Value (Pointer : aliased in Safe_Pointer) return access Object;

with a body of

    function Value (Pointer : aliased in Safe_Pointer) return access Object is
    begin
        return Pointer.Ptr;
    end Value;

This is safe because of the accessibility checks. The Pointer parameter to Value
will be required to live longer than the function result. Typically, the
function result will have a very local lifetime. That means that it cannot be
assigned to any other access type, so about all that can be done with it is to
dereference it. If you do manage to use the value in a context (that's easier
for "access Object" than for the discriminant case) where it lives a long time,
the parameter has to live as long or longer, so it stays safe.

Note that you don't need "in out" here, because you aren't (ever) going to
return part of the actual object. If your Safe_Pointer type could be defined:
    type Safe_Pointer is record
        Obj : aliased Object;
        Refs : Natural := 0;
        ...
    end record;
then you would need "in out". (That can't happen in this case, but it can for
the containers.)

Note that you'd probably want to declare Safe_Pointer "tagged" so that your
users wouldn't have to declare every object as "aliased".

For this definition, I'm assuming that there aren't any operations that can
invalidate an existing Safe_Pointer object. If such operations exist (seems
likely), then you would want a more complex scheme where you can keep track of
the existence of the (raw) pointer:

   type Accessor_Type (Element: not null access Object) is tagged limited private;

   function Value (Pointer: aliased in out Safe_Pointer) return Accessor_Type;

For uses of Value, generally you would ignore the Accessor_Type object and just
dereference the discriminant directly:

   ... Value (My_Ptr).Element.all ...

We'd add a Raw_Refs counter to the Safe_Pointer:

    type Safe_Pointer is tagged record
        Ptr : Unsafe_Pointer;
        Refs : Natural := 0;
        Raw_Refs : Natural := 0;
        ...
    end record;

Accessor_Type would be defined as limited controlled so that you get an
indication when the access ceases to exist:

   type Accessor_Type (Element: not null access Object) is new Ada.Finalization.Limited_Controlled with record
       My_Ptr : not null access Safe_Pointer; -- Default initialization will raise Constraint_Error.
   end record;

   procedure Finalize (Object : in out Accessor_Type);

We want a default-initialized object of Accessor_Type to raise an exception, as
we only want Value to create one. We can't use the unknown discriminants (<>)
trick here, as the whole point is the discriminant.

And the bodies:

   function Value (Pointer: aliased in out Safe_Pointer) return Accessor_Type is
   begin
       Pointer.Raw_Refs := Pointer.Raw_Refs + 1;
       return (Element => Pointer.Ptr, My_Ptr => Pointer'Access);
   end Value;

   procedure Finalize (Object : in out Accessor_Type) is
   begin
       Object.My_Ptr.Raw_Refs := Object.My_Ptr.Raw_Refs - 1;
	    -- Will raise Constraint_Error if there is an underflow, that shouldn't be possible.
   end Finalize;

Then, any operation that invalidates a Safe_Pointer object should first check
that the Raw_Refs counter is equal to 0; if it is not, an exception should be
raised instead. (The containers raise Program_Error for a "tampering" event.)

This way, once someone creates an access value to access the actual object, the
Safe_Pointer will be "locked" against modification until the access value is no
longer accessible (that is, the locking will continue so long as the access
value exists).

Of course, there are a number of ways to circumvent this safety (using
Unchecked_Deallocation, Unchecked_Access, or Unchecked_Conversion). But that is
true for any Ada feature; one has to assume that the programmers are not
intentionally shooting themselves in the foot. (Uncontrolled use of "Unchecked"
anything is dangerous - that's why they're called "Unchecked", after all.)

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

Summary of private mail on accessibility wording (April-June 2010)

==============

Randy Brukardt:

I spent some time in the shower today thinking about this problem. I think I've
concluded that the only problem was my note, but I might have missed something.
Here's my analysis. (Now I've flip-flopped. Grrrr.)

(1) We only need to worry about the dynamic check in cases where the static
    check succeeds. If it fails, no one is ever going to run the code.

(2) If we can prove that the dynamic check always has to succeed, it becomes
    irrelevant.

So the goal is to prove that the static check fails or the dynamic check always
succeeds.

The rules in question are for checking the accessibility of actuals passed to
aliased parameters, checked at the call site. The case that I was worried about
involves formal parameters passed in this circumstance. This necessarily has to
happen in a body (in a specification, as in an aspect clause, the real actuals
are available and they can be checked without extra overhead, so I don't much
care). This would look something like:

   function Func_A (R : aliased T) return S;

   type Ptr_S is access all S;
   P_S : Ptr_S;

   function Func_B (P : aliased T; Q : T) return access S is
       O1 : S := Func_A (P); -- OK (1A)
       O2 : S := Func_A (Q); -- OK (1B)
   begin
       P_S := new S'(Func_A (P)); -- Statically illegal?? (2A)
       P_S := new S'(Func_A (Q)); -- Statically illegal (2B)
       if ... then
           return P'access; -- OK (3A)
       elsif ... then
           return new S'(Func_A (P)); -- OK (3B)
       else
           return new S'(Func_A (Q)); -- Statically fails?? (3C)
       end if;
   end Func_B;

These are most of the cases I can think of. I'm assuming AI-51 is adopted in the
below. I'm also assuming that the static check uses the declared level of the
formal parameters (other than anonymous access, which already uses the dynamic
check, nothing new there). I didn't try to prove the latter, but I think it has
to work that way currently, and we're not changing that.

For local calls (the common case), like 1A and 1B, the static check will succeed
because the formal parameters have the same level (or are outside of in the case
of nesting and for aliased parameters of functions) the calls. The dynamic check
will always succeed, as the objects that are passed have to live dynamically
longer than the call (they were created before it). So no dynamic check is
actually required.

For allocators of outer (named) access types, we need to look at both cases
separately as well. For a normal parameter (2B), the accessibility is that of
the function. Thus, the static check has to fail (the access type has to have a
longer lifetime than the parameter).

For an aliased parameter, the accessibility level is defined to be the same as
that of the function result. Because of AI-51, that level is dynamic. That means
that we can't do a static check here (right?). We therefore would have to do a
check based on the accessibility of the actual of the formal P, and that check
could fail. This we don't want to have to do.

However, what I intended here is that we would do a static check based on the
formal level of the parameter (that is, the level of the function result, which
is shallower than the function but deeper than everything else). That static
check would necessarily fail (any named type that is outside of the function can
be assumed to have a longer lifetime than the function call), so nothing dynamic
would be necessary. How do we get the wording to reflect this?

Anyway, moving on. Is there a way to force a dynamic check here even with the
static rule I suggest above? That requires an access type with dynamic
accessibility. If the type is declared in a generic unit, it technically has a
dynamic check in the instance, but since the static check in the generic body
would fail, the dynamic check is moot. Similarly for anonymous access
components. Anonymous access parameters allocate at the call site which is the
local case (and can't be allocated in the body, as the parameter is read-only),
so there is no problem with them. I believe the same is true for stand-alone
anonymous access objects. I think the access discriminant rules boil down to
where the object is declared, so they end up being one of the other cases
(including the return one, yet to discuss). So I don't think there is a problem
here.


For return statements, the level of the return object is dynamic. Since return
objects have dynamic accessibility, there is not (usually??) a static check. For
just returning an access to a parameter (3A, only legal for aliased parameters),
there is no static check, but there needs to be no dynamic check either, since
the accessibility levels are defined to be the same. The same is true for the
function initializing an allocator of the function result using the aliased
parameter (3B).

OTOH, we don't know the result of the dynamic check for a function initializing
an allocator of the function result using the normal parameter (3C). But here
again we have a problem: we arguably should be using the level of the actual for
the dynamic check. It would be better if the static check failed here (the
formal parameter always having accessibility that is deeper than that of the
function result, even though we don't know exactly what that is), but I don't
think the statically deeper rules work in this case, as there is no defined
static level for the function result. (Not completely sure, I don't understand
AI-51 enough to be certain.)

So the primary problem is to ensure that the static check is triggered (and
fails) in these two cases (2A) and (3C), so that we never have to worry about
the dynamic check (and thus incur no overhead).

===============

Tucker Taft:

   Here is a slightly reworded version of the piece of AI-142 that I think you
   are concerned about:

Add after 6.4.1(15): [Dynamic Semantics] (this would be a new, outer level,
bullet)

* In a function call, for each explicitly aliased parameter, a check is made
  that the accessibility level of the master of the actual object is not deeper
  than that of the master of the function result.

AARM To Be Honest: We're talking about the "nominal" level of the actual object.
If the actual object is a formal parameter of some function call F, we do not
intend to require dynamic checks that depend on the master of the actual
parameters to F, as that would cause nasty distributed overhead (all tagged and
explicitly aliased parameters would have to carry accessibility levels).

[Q: Is there a way to reword this to make the above clear?? Masters are usually
a dynamic concept, but here we only want to use the master we know, not the
actual master of the underlying object (which could be different).]

------------

Your desire is to get rid of the "To Be Honest" I believe.
You put it in because you want to be sure this is done efficiently, but I
believe you now think that the expensive cases will either fail the static
check, or always pass the dynamic check, so we don't need to explicitly allow
any shortcuts in the dynamic check.

I believe I agree with you.  I will try to explain myself using the following
example, which you provided in one of your various e-mails:

    function Func_A (R : aliased T) return S;

    type Ptr_S is access all S;
    P_S : Ptr_S;

    function Func_B (P : aliased T; Q : T) return access S is
        O1 : S := Func_A (P); -- OK (1A)
        O2 : S := Func_A (Q); -- OK (1B)
    begin
        P_S := new S'(Func_A (P)); -- Statically illegal?? (2A)
        P_S := new S'(Func_A (Q)); -- Statically illegal (2B)
        if ... then
            return P'access; -- OK (3A)
        elsif ... then
            return new S'(Func_A (P)); -- OK (3B)
        else
            return new S'(Func_A (Q)); -- Statically fails?? (3C)
        end if;
    end Func_B;

----------------

First of all, (3A) is illegal as written because P is of type T but we are
returning access S.  But presuming P were of type S, then I agree this should
pass both the static and dynamic checks, since that is pretty much the whole
point of this AI!

AI-51 requires the caller to pass in a dynamic accessibility level for functions
with access results (or with result objects potentially having access
discriminants). The rules of this AI make certain that any explicitly aliased
parameters have dynamic accessibility no deeper than this passed-in value for
the accessibility of the return object. (We may need to add a special case in
the definition of "statically deeper than" to make sure the static check comes
out right -- somewhere in the vicinity of 3.10.2(19) -- see below.)

Now back to (2A).  Except for the special case of comparing against the level of
the return object, explicitly aliased parameters are presumably considered
equivalent to any other parameter as far as the static accessibility level, and
hence are statically deeper than P_S. So I agree (2A) is just like (2B) as far
as the static check, and both fail.

We already talked about (3A) above.  (3B) is more complex. First of all I
presume type S is build-in- place, and so we can't just copy the result of
calling Func_A, at least not without worrying about access discriminants.  So we
care what is the accessibility level of the return object of the call on Func_A.
But by AI-51, we pass in the required accessibility level for the return object,
and the level we pass in is that passed in to Func_B, and we know P's
accessibility level is no deeper than that, so everything is fine as far as
dynamic accessibility levels.

What about static accessibility checks for (3B)?
  From a static point of view, the level of the return from Func_B is that of
  the master of Func_B, and ditto for Func_A, so we are fine since they have the
  same master.  However, if Func_A were nested inside of Func_B, then this would
  fail the static check.

Now on to (3C).  In this one the call on Func_A fails the static check specified
by this AI (added after 6.4.1(6), since the return object of the call on Func_A
has a static level from the master of Func_A, and Q is clearly deeper than that.

As indicated above, I do think we need to add something near 3.10.2(19).  Here
is the addition from AI-51:

Add after 3.10.2(19/2):

     * For determining whether a level is statically deeper than the
       level of the anonymous access type of an access result of a function,
       when within a return statement that applies to the function, the
       level determined by the point of call is presumed to be the same
       as that of the level of the master that elaborated the function
       body.

I think we need to revise this to be something like:

     * Inside a return statement that applies to a function
       with an access result, when determining whether the
       accessibility level of an explicitly aliased parameter
       of the function is statically deeper than the level of the
       anonymous access type of the access result, the level
       determined by the point of call is presumed to be the same
       as that of the level of the explicitly aliased parameter;
       for statically comparing with the level of other entities,
       the level determined by the point of call is presumed to
       be the same as that of the master that elaborated the
       function body.

I think this may need to be generalized to include types that could have access
discriminants (e.g. all class-wide types).

===========

Randy Brukardt:

> Your desire is to get rid of the "To Be Honest" I believe.
> You put it in because you want to be sure this is done efficiently,
> but I believe you now think that the expensive cases will either fail
> the static check, or always pass the dynamic check, so we don't need
> to explicitly allow any shortcuts in the dynamic check.

Right. So I think what I have to do is reword the To Be Honest into a
Ramification, noting that the dynamic check cannot fail for a formal parameter
unless the operation is statically illegal. Thus no overhead of dynamic checking
is needed for aliased parameters.

...
> I think this may need to be generalized to include types that could
> have access discriminants (e.g. all class-wide types).

Not sure what you mean here. Are you saying that the part about "with an access
result" should be dropped? That makes sense to me - I'm not sure why this is
limited to access results, don't all return objects have the same effective
level?? Sure, some don't have anything interesting that can be done, but so
what?

      * Inside a return statement that applies to a function F, when
        determining whether the accessibility level of an explicitly
        aliased parameter of F is statically deeper than the level of the
        return object of F, the level of the return object is presumed to
        be the same as that of the level of the explicitly aliased
        parameter; for statically comparing with the level of other
        entities, the level of the explicitly aliased parameter of F is
        presumed to be the same as that of the master that elaborated
        the function body of F.

I dropped the "point of call" stuff because it doesn't seem to be helpful.

Remind me: is the last part the same as normal parameters? That is, it would be
weird if the static accessibility of a parameter differed when it was in a
return statement or outside.

Finally, if we can't talk about the level of the return object, then we ought to
talk about the level of an access result or access discriminant. Then, if
neither apply we drop into the "normal" case, which is fine.

==========

Tucker Taft:

>       * Inside a return statement that applies to a function F, when
>         determining whether the accessibility level of an explicitly
>         aliased parameter of F is statically deeper than the level of the
>         return object of F, the level of the return object is presumed to
>         be the same as that of the level of the explicitly aliased
>         parameter; for statically comparing with the level of other
>         entities, the level of the explicitly aliased parameter of F is
>         presumed to be the same as that of the master that elaborated
>         the function body of F.

Oops, you garbled this a bit.
Should be:
      ... for statically comparing with the level of
      other entities, the level of the return object is
      presumed to be the same as that of the master that
      elaborated the function body of F.

If we want to say something more about explicitly aliased parameters, we should
say that "in other contexts, their level is the same as that of other
parameters."

> I dropped the "point of call" stuff because it doesn't seem to be helpful.

OK.

> Remind me: is the last part the same as normal parameters? That is, it
> would be weird if the static accessibility of a parameter differed
> when it was in a return statement or outside.

It is only within a return statement that this special case exists, and only for
explicitly aliased parameters.

> Finally, if we can't talk about the level of the return object, then
> we ought to talk about the level of an access result or access discriminant.
> Then, if neither apply we drop into the "normal" case, which is fine.

I think we can probably get away with the wording you propose, though probably
this will be revised at least once more after Steve Baird et al get a chance to
review it.

===========

Randy Brukardt:

I see, the wording was mainly about return objects, and not explicitly aliased
parameters. I missed that originally (the wording order is a bit weird). But I
think I'll leave it and let Steve and others pull it apart.

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

Questions? Ask the ACAA Technical Agent