Version 1.1 of ai05s/ai05-0142-3.txt
!standard 3.10.2(13.1/2) 09-04-10 AI05-0142-3/01
!class Amendment 09-04-10
!status work item 09-04-10
!status received 09-04-10
!priority Medium
!difficulty Medium
!subject Accessors for Ada.Containers
!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 to define a procedure for every component of the element
that needs changing. 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. 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
(See wording.)
!wording
[This proposal grew out of an extensive phone discussion between Steve Baird and Randy Brukardt
on the evening of Thursday, April 9.]
Pseudo-wording:
Static semantics: For non-in parameters of a function call, the master of the actual
parameter is that of the function result (rather than the master of the function call).
[The master of a function result is defined by 3.10.2(10/2). Note that if the
function call is a master, these two masters will be different.]
Legality Rule: In a function call, the accessibility level of the actual object for each
non-in parameter shall not be statically deeper than accessibility level of the master
of the function result.
Run-time check: In a function call, for each non-in parameter, a check is made that the
master of the accessibility level of the actual object is the same as or includes the
master of the function result.
[For these two rules, we use "accessibility level" to make it clear that we are talking about the
nominal master of the actual object. In particular, we do not want to require dynamic checks
where we would need to know the master of the actual that corresponds to a formal object.
This wording might need some tweaking to make that clear.]
The accessibility level of a formal non-in parameter in a function is that that the return
object will have after a return statement completes normally.
[That level is defined by 3.10.2(10.1/2)].
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 : access Element_Type) is tagged limited private;
Accessor_Type needs finalization.
Finalization of a default-initialized object of type Accessor_Type has no effect. [We need
to say this in order to define what happens if someone declares one of these by hand.]
AARM Implementation Note: 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
and nothing should happen.
function Accessor (Container : in out 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 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 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 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.)
!discussion
The idea is that the accessibility level of non-in 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).
---
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 := new MV.Accessor_Type'(Vect.Accessor (1))
Element : access Element_Type := PAT.Element; --
begin
Free (PAT); --
Vect.Delete (1); --
... Element ... --
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.
---
By tying the new semantics to the use of in out and out parameters in functions, we avoid
any incompatibility (as those aren't allowed in Ada currently). We also avoid the need to
define any new syntax associated with accessor functions.
However, by using only in out parameters in the new semantics, we make it harder to creating
read-only accessors. We could not create an accessor like:
type RO_Accessor_Type (Element : access constant Element_Type) is tagged limited private;
function Read_Only (Container : Vector; Index : Index_Type) return RO_Accessor_Type;
because a part of the parameter could not be returned - the accessibility check would fail for
the in parameter. That would not work for a bounded vector. However:
function Read_Only (Position : Cursor) return RO_Accessor_Type;
could be defined (as a reference to the container is an implicit part of the cursor, and that
could be used to access the appropriate part of the container).
The main problem with making this master change for all parameters of functions is that it
could 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. In addition, the accessibility check needed could cause some functions used
to initialize an allocator to become illegal. Thus we don't change the master of all
parameters.
---
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 Vector; I : Index_Type) return access Element_Type;
Now consider the following program fragments:
package Data is
type Node (D : Boolean := True) is record
case D is
when True =>
CT : Natural;
when False =>
CF : Float;
end case;
end record;
subtype True_Node is Node (D => True);
subtype False_Node is Node (D => False);
package Node_Vector is new Ada.Containers.Vectors (Element_Type => Node,
Index_Type => 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 := <lengthy-computation-resulting-in-value-of-1>;
...
Data.Node_List.Delete (Some_Index);
...
end Some_Complex_Operation;
end Far_Away;
with Data;
package Process is
procedure Process_True (NT : in out Data.True_Node);
end Process;
with Far_Away;
package body Process is
procedure Process_True (NT : in out Data.True_Node) is
Component : renames NT.CT; --
begin
...
Far_Away.Some_Complex_Operation (...);
if Component > 10 then --
...
end if;
end Process_True;
end Process;
with Data, Process;
procedure Main is
begin
Data.Node_List.Append (New_Item => (D => False, CF => 1.0)); --
Data.Node_List.Append (New_Item => (D => True, CT => 4)); --
Data.Node_List.Append (New_Item => (D => False, CF => 26.5)); --
...
Process.Process_True (Data.Node_List.Accessor (2).all);
...
end Main;
Now, in the canonical implementation for a vector, all of the data is stored in
an array of some size, and when a component is deleted, the rest of them slide
down. So, when the Far_Away operation (probably called through many levels of
calls) deletes the first element of the vector, all of the rest of them move.
That means that the second element passed to Process_True unexpectedly changes
its value to that which was in the third element. But that element doesn't match
the constraint and doesn't even have a CT component! So we get erroneous
behavior. Note that the same thing would happen if an element was inserted at
the front of the vector.
Note that in this example no one has copied the access value returned by the accessor.
So a rule preventing copying does no good. You can also get a similar
effect by renaming the access value itself and doing bad things while the
renames exists.
Note that the erroneousness can't happen if you use Update_Element instead,
because the tampering check will cause the deletion to raise Program_Error.
Similarly, cursors have rules allowing implementations to detect this sliding
and raise Program_Error.
This example code seems plausible; the record type is similar to many found in the
author's compiler and using a library-level container is not unusual. So it seems
reasonable that cases like this would happen in practice. Thus we conclude that
a tampering check is needed for the accessor functions.
---
Alternatives:
It would be nice to restrict the accessibility check on non-in function parameters
to only functions that could usefully return an access to some part of the parameter.
One way to do that would be to make the accessibility changes only apply to parameters
of functions that have anonynous access results or have return objects with access
discriminants.
However, because this property is not part of mode-conformance, such a rule would
cause contract problems for generics. It would not be possible in a
generic body to tell if the rule applied to a particular function or not:
generic
type Cont is private;
type Lim (<>) is limited private;
with function Accessor (C : in out Cont) return Lim;
package G is
procedure Something;
end G;
package body G is
type ALim is access Lim;
procedure Something is
C : Cont;
A : ALim;
begin
A := new Lim'(Accessor (C));
end Something;
end G;
package Inst1 is new G (MV.Vector, MV.Accessor_Type, MV.Accessor);
--
package Inst2 is new G (Natural, Natural, Some_Func);
--
We could try to apply an assume-the-worst rule, but that essentially turns into
the original rule with no conditionality. So the complication doesn't buy much,
and it would be a nightmare to implement in a shared generic.
Similar problems beset a rule using immutably limited types as the differentiator.
Another option that was considered was to have a method to mark subprogram parameters
that need this capability. As this compatibility is an important part of the profile
that ought to figure into mode conformance, this would need to be done with syntax
(having a pragma affect legality is uncomfortable).
This would have the advantage of applying the accessibility check only to parameters
that actually need it, improving flexibility. But adding syntax for a rarely-used case
is annoying at best:
function Accessor (Container : in out Vector use for return; Position : in Cursor)
return Accessor_Type;
function Read_Only (Container : Vector use for return; Index : Index_Type)
return RO_Accessor_Type;
The idea is that "use for return" would be a "mode-modifier", which would be considered
as part of profile for conformance purposes. Thus it would have to be included in
renames and for generic matching. That neatly gets rid of the generic contract issues,
but of course at the cost of extra syntax.
!examples
The new Accessor function could be used (based on a recent example from comp.lang.ada):
with Ada.Containers.Vectors;
procedure Check is
package Integer_Vectors is
new Ada.Containers.Vectors
(Index_Type => Natural,
Element_Type => Integer);
package Nested_Vectors is
new Ada.Containers.Vectors
(Index_Type => Natural,
Element_Type => Integer_Vectors.Vector,
"=" => Integer_Vectors."=");
IV : Integer_Vectors.Vector;
NV : Nested_Vectors.Vector;
begin
IV.Append(42);
NV.Append(IV);
NV.Accessor(0).Element.Append(43);
end Check;
The dereference does not need to be explicitly given in this case, and the fact that
the object returned by Accessor is limited and (probably) controlled is not visible
in the code.
You could also save the entire object as long as needed:
declare
My_IV : Nested_Vectors.Accessor_Type := NV.Accessor(0);
begin
(since My_NV would be built-in-place).
Note that this design still works as intended even if the user renames only the limited access value:
declare
My_IV : Integer_Vectors.Vector renames NV.Accessor(0).Element.all;
begin
Even in this case, the wrapping finalizable object still is protecting the container, because
the master of the returned object is the master of the renames: it will stick around as long
as the renames does.
Moreover, splitting the access from the object usually isn't possible:
declare
My_IV : access Integer_Vectors.Vector := NV.Accessor(0).Element; --
begin
This is illegal because the accessibility check fails. The (anonymous) object returned here
will have the master of the expression, which is shorter than the master of the declaration
of My_IV. Thus the accessibility check preventing shorter lifetimes will fail.
It is OK to rename the access:
declare
My_IV : access Integer_Vectors.Vector renames NV.Accessor(0).Element;
begin
as in this case, the anonymous object will continue to exist until the renames is finalized.
This element can be passed as a parameter, as again the wrapping object will live as long as
the parameter:
Operate (NV.Accessor(0).Element);
or
Munge (NV.Accessor(0).Element.all);
!ACATS test
ACATS tests would be needed for this feature.
!appendix
From: Steve Baird
Sent: Friday, April 10, 2009 11:12 AM
> I think adding "call-level" accessibility is a smaller change, and is
> relatively intuitive, so I would rather do that than add a whole
> "limited access type" concept.
I spoke with Randy yesterday and I believe that we both agree with you on this
point. At the risk of repeating much that has been said before, I'll summarize
where I think we stand now.
As you pointed out, no language changes are needed in order to get the desired
functionality in the case where the implementation of the container already uses
a level of indirection.
This is accomplished by using a function whose result type is tagged limited
private with a visible access-to-element discriminant and with a finalization
routine that updates the tampering state of the container.
Two language changes are needed in the case where the objects that need to be
referenced (as variables) in the function result have the accessibility level of
the container parameter (as opposed to the accessibility level of the container
generic instance, as would typically be the case if a level of indirection is
introduced):
- in out parameters for functions
- a new parameter accessibility rule which has the effect
of allowing an immutably limited function result
to contain references to (some) parameters; finalization of any
referenced actual parameter objects must be correspondingly
deferred until after the finalization of the function result object.
Both of these are needed in order to support something like
type Element_Handle (Element_Ref : access Element)
is tagged limited private;
function F (C : in out Container; I : Index) return Element_Handle is
begin
return (Element_Ref => C.Elements(I)'Access, ...);
-- result also includes reference
-- to C or perhaps C.Tampering_State
end F;
In whatever cases we decide this new accessibility rule applies, this means that
the finalization of the actual parameter may occur later than it otherwise would
in the case where the actual parameter is a function call, an aggregate, or an
allocator of an anonymous access type.
Is this compatibility issue a major concern?
****************************************************************
From: Tucker Taft
Sent: Friday, April 10, 2009 11:55 AM
> Is this compatibility issue a major concern?
Not to me. Since I think the result type will inevitably need to be a
build-in-place type, these didn't exist before Ada 2005, so the compatibility
issues are trivial at this point.
Note that I have proposed that access results have essentially the same rules as
the access discriminants of build-in-place results, so this would allow for
un-wrapped access results to be used in those situations where no finalization
is required, accomplishing essentially everything we would get from limited
access types.
****************************************************************
From: Randy Brukardt
Sent: Friday, April 10, 2009 1:09 PM
...
> Note that I have proposed that access results have essentially the
> same rules as the access discriminants of build-in-place results, so
> this would allow for un-wrapped access results to be used in those
> situations where no finalization is required, accomplishing
> essentially everything we would get from limited access types.
This is not true, in that you have no control over the accessibility of the
result. The rules you proposed were that the accessibility of the result was
that of the type that the access result is converted to. That's not necessarily
the same as the place of the call. In the built-in-place object case, you can
*only* get the accessibility of the call.
That means that an implementation change in a package could cause functions that
previously worked to raise Program_Error. This is clearly a maintenance hazard.
To take an example, for a library level container C and access type Acc and the
following function Func:
function Func (C : in out Container) return access Element_Type;
declare
Ptr : Acc := Func (C); -- (1)
begin
This call will work if the container is implemented with nodes allocated out of
a library-level heap (as is the case with the Unbounded containers), and will
raise Program_Error if the container is implemented with an internal array of
nodes directly as part of the object (as is the case with the Bounded
containers). This means that if the implementation of the container changes,
calls might start raising Program_Error for no apparent reason.
OTOH, the access discriminant case:
declare
Ptr : Acc := Func (C).Element;
begin
will statically fail an accessibilty check (as the discriminanted object has a
nested scope, and the access type's is library level).
Of course, we're not going to use the former form, so this doesn't matter for
Ada.Containers.
****************************************************************
From: Tucker Taft
Sent: Friday, April 10, 2009 1:39 PM
...
> This is not true, in that you have no control over the accessibility
> of the result. The rules you proposed were that the accessibility of
> the result was that of the type that the access result is converted
> to. That's not necessarily the same as the place of the call. In the
> built-in-place object case, you can *only* get the accessibility of the call.
I don't agree with this. You can use a call on a build-result-in-place function
to initialize an allocator of a named access type, and you can return it from an
outer build-result-in-place function. In both of these cases, you don't have
call accessibility. In general, the caller must indicate whether the context
corresponds to "call-level" accessibility or something longer-lasting. On
function return, if the function returns 'Access of some part of an actual
parameter, then it will have to raise Program_Error if the caller context
indicates something longer lasting that call-level accessibility.
I believe this is exactly analogous to the case with access results. If you
dereference the access result directly, (perhaps to define a rename), you have
call-level accessibility. If you convert it to some named type, you don't have
call-level accessibility. Similarly, if you use a call of a
build-result-in-place function to initialize an allocator for a named access
type, then you don't have call-level accessibility. If you pass it to another
function, then you do.
...
> OTOH, the access discriminant case:
>
> declare
> Ptr : Acc := Func (C).Element;
> begin
>
> will statically fail an accessibilty check (as the discriminanted
> object has a nested scope, and the access type's is library level).
But what about:
declare
Ptr : Acc_T := new T'(Func(C));
begin
This will have the same issue as the access result.
I really believe they are analogous, with similar accessibility rules, if you
map "used to initialize allocator of named access type" in the build-in-place
case to "converted to named access type" in the access result case.
> Of course, we're not going to use the former form, so this doesn't
> matter for Ada.Containers.
I think you have the same issue using a build-in-place result, but I agree that
using it to initialize an allocator of a named access type is perhaps less
likely in practice than simply converting an access result to a named access
type. The key point in my mind is that the language rules are analogous, so
presumably similar implementation techniques can be used, and the user can shift
between returning an access result and returning a build-in-place object without
huge shifts in capability creating annoying false incentives.
****************************************************************
From: Randy Brukardt
Sent: Friday, April 10, 2009 2:05 PM
...
> I don't agree with this. You can use a call on a
> build-result-in-place function to initialize an allocator of a named
> access type, and you can return it from an outer build-result-in-place
> function. In both of these cases, you don't have call accessibility.
I don't see this, although that may be because I've already abandoned the entire
idea of call-level accessibility (we don't need it). I'll be making a full(er)
write-up right after lunch and you will see.
My model is that in all cases, the accessibility of the call is that of the
function result. That means in the case of an allocator the master of the call
is the master of the access type. So things had better be long-lived.
In any case, the "outer built-in-place" function case had better work without
raising any funny exceptions. It makes perfect sense to nest these guys (in the
container-of-containers case), and I consider that a critical requirement.
> In general, the caller must
> indicate whether the context corresponds to "call-level"
> accessibility or something longer-lasting.
There is nothing longer-lasting here; we're just talking about the accessibility
of the function result. Period. (The more general notion of call-level
accessibility is now dropped, we don't need it.)
> On function return, if the function returns 'Access of some part of an
> actual parameter, then it will have to raise Program_Error if the
> caller context indicates something longer lasting that call-level
> accessibility.
No. I do think this shows that there may be a need to do an accessibility check
*on the parameter* in a few unusual cases. But that check can usually be done
statically, so it is a big improvement over the normal case.
> I believe this is exactly analogous to the case with access results.
> If you dereference the access result directly, (perhaps to define a
> rename), you have call-level accessibility.
> If you convert it to some named type, you don't have call-level
> accessibility. Similarly, if you use a call of a
> build-result-in-place function to initialize an allocator for a named
> access type, then you don't have call-level accessibility. If you
> pass it to another function, then you do.
None of this makes any sense to me. We obviously have a different idea of the
accessibility rules here.
...
> But what about:
>
> declare
> Ptr : Acc_T := new T'(Func(C));
> begin
>
> This will have the same issue as the access result.
Not unless C is a local object. The accessibility of the function result in this
case is library level. That's the master of the formal parameter as well. Since
that is the case, I think there would have to be an accessibility check on the
parameter (so that *it* cannot be a more local than the function result).
..
> I think you have the same issue using a build-in-place result, but I
> agree that using it to initialize an allocator of a named access type
> is perhaps less likely in practice than simply converting an access
> result to a named access type. The key point in my mind is that the
> language rules are analogous, so presumably similar implementation
> techniques can be used, and the user can shift between returning an
> access result and returning a build-in-place object without huge
> shifts in capability creating annoying false incentives.
I agree with this, in that the same rules of accessibility should apply in both
cases. We just don't have quite the same idea of the accessibility rules needed.
But raising Program_Error conditionally depending on the implementation of the
containers is clearly unacceptable. If the accessibility is wrong, that should
*always* be detected (even if the actual implementation would allow it to work).
I think that the rules I have in mind would have that effect for the containers
(yours definitely do not).
****************************************************************
From: Tucker Taft
Sent: Friday, April 10, 2009 3:35 PM
...
> My model is that in all cases, the accessibility of the call is that
> of the function result. That means in the case of an allocator the
> master of the call is the master of the access type. So things had better be long-lived.
You will have to define what you mean by "accessibility of the call." I am
guessing this becomes a level that some or all of the actual parameters are
being checked against. Perhaps the checks would apply only to those parameters
that will have some aliased part or coextension that might be designated by some
access value buried in the result object? This sounds hard to specify.
The existing Ada 2005 rules for build-in-place clearly have the context of the
call determining where the return object is created, be it in the heap, as a
component of another object, in a declared variable, or as a temp passed to
another subprogram. I presume you aren't planning on changing that. Instead it
sounds like you are going to specify that (some or all?) of the (by-reference?)
parameters must live at least as long at the return object.
...
>> I believe this is exactly analogous to the case with access results.
>> If you dereference the access result directly, (perhaps to define a
>> rename), you have call-level accessibility.
>> If you convert it to some named type, you don't have call-level
>> accessibility. Similarly, if you use a call of a
>> build-result-in-place function to initialize an allocator for a named
>> access type, then you don't have call-level accessibility. If you
>> pass it to another function, then you do.
>
> None of this makes any sense to me. We obviously have a different idea
> of the accessibility rules here.
I think the difference is that I was presuming no restrictions were being
imposed at the call site on the parameters, whereas you are now proposing that
there might be some accessibility checks on the parameters. That certainly
sounds like an approach worth considering. But everything I wrote earlier has
presumed no such checks, so all of the checking had to be performed at the
return statement, potentially using information passed in from the caller. I'm
not saying that is better, but it might explain the difference in perspective.
>
> ...
>> But what about:
>>
>> declare
>> Ptr : Acc_T := new T'(Func(C));
>> begin
>>
>> This will have the same issue as the access result.
>
> Not unless C is a local object. The accessibility of the function
> result in this case is library level. That's the master of the formal
> parameter as well. Since that is the case, I think there would have to
> be an accessibility check on the parameter (so that *it* cannot be a
> more local than the function result).
Yes, I am beginning to understand your model. It basically assumes the worst
about what might be used with 'Access in the return statement.
> ..
>> I think you have the same issue using a build-in-place result, but I
>> agree that using it to initialize an allocator of a named access type
>> is perhaps less likely in practice than simply converting an access
>> result to a named access type. The key point in my mind is that the
>> language rules are analogous, so presumably similar implementation
>> techniques can be used, and the user can shift between returning an
>> access result and returning a build-in-place object without huge
>> shifts in capability creating annoying false incentives.
>
> I agree with this, in that the same rules of accessibility should
> apply in both cases. We just don't have quite the same idea of the
> accessibility rules needed. But raising Program_Error conditionally
> depending on the implementation of the containers is clearly
> unacceptable. If the accessibility is wrong, that should *always* be
> detected (even if the actual implementation would allow it to work). I
> think that the rules I have in mind would have that effect for the containers (yours definitely do not).
I think we aren't that far off, really. If we can agree that access results
should piggy-back on the rules for access discriminants of build-in-place return
objects, I'm happy. If we decide to impose some accessibility checks on the
actual parameters when calling a build-in-place function, those same checks
would be relevant for a function with an access result.
In either case, the delicate part will be defining which actual parameters are
to be checked, and that will in turn imply which formal parameters can be
referenced with 'Access in the return statement.
****************************************************************
From: Randy Brukardt
Sent: Friday, April 10, 2009 10:06 PM
Find attached a full write-up of the latest ideas on this topic. [This is /01
of this AI - ED]
I'm not completely certain that the accessibility check is harmless in the case
of functions that don't need it, but the only way to get rid of it is to add a bit
of syntax to differentiate between parameters that need it and those that don't.
(See the very end of the !discussion section.) That seems like overkill in the
absence of a real problem.
Tell me what you think.
****************************************************************
From: Randy Brukardt
Sent: Friday, April 10, 2009 6:40 PM
****************************************************************
From: Randy Brukardt
Sent: Friday, April 10, 2009 6:40 PM
****************************************************************
From: Randy Brukardt
Sent: Friday, April 10, 2009 6:40 PM
****************************************************************
From: Randy Brukardt
Sent: Friday, April 10, 2009 6:40 PM
****************************************************************
From: Randy Brukardt
Sent: Friday, April 10, 2009 6:40 PM
****************************************************************
From: Randy Brukardt
Sent: Friday, April 10, 2009 6:40 PM
****************************************************************
From: Randy Brukardt
Sent: Friday, April 10, 2009 6:40 PM
****************************************************************
From: Randy Brukardt
Sent: Friday, April 10, 2009 6:40 PM
****************************************************************
From: Randy Brukardt
Sent: Friday, April 10, 2009 6:40 PM
****************************************************************
From: Randy Brukardt
Sent: Friday, April 10, 2009 6:40 PM
****************************************************************
Questions? Ask the ACAA Technical Agent