CVS difference for ai05s/ai05-0142-3.txt

Differences between 1.1 and version 1.2
Log of other versions for file ai05s/ai05-0142-3.txt

--- ai05s/ai05-0142-3.txt	2009/04/11 03:08:17	1.1
+++ ai05s/ai05-0142-3.txt	2009/04/28 05:38:49	1.2
@@ -1,4 +1,4 @@
-!standard 3.10.2(13.1/2)                                   09-04-10  AI05-0142-3/01
+!standard 3.10.2(13.1/2)                               09-04-27  AI05-0142-3/02
 !class Amendment 09-04-10
 !status work item 09-04-10
 !status received 09-04-10
@@ -43,99 +43,222 @@
 
 !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).
+Language Design Principles: The idea of the returnable parameter (see below) is
+that 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 a
+combination of static and dynamic rules (most of the dynamic rules can actually
+be evaluated at compiler-time, but doing so would break privacy or the generic
+contract -- we don't want legality rules that do either of these things.) Note
+that these rules don't need to be a perfect match between the call and inside
+the function -- it is harmless if the call thinks a parameter if returnable
+while the actual function body does not (this can happen inside of a generic,
+for instance). On the other hand, disaster could result if the call thinks it is
+passing a normal parameter (and thus makes no checks) while the body assumes the
+parameter was already checked.
+
+[I defined the terms "returnable parameter" etc. below in order that we don't
+distribute that definition all over the world. Currently, they only get used a
+few times, but that's fine. - RLB]
+
+Static Semantics (Definitions): A "returnable parameter" is one for which a part
+(or coextension) of the parameter can be returned as a function result. An
+"immutably returnable parameter" is a parameter with the following
+characteristics:
+   * it is a parameter of a function with an anonymous access result, or a
+     result subtype whose view has a part with an access discriminant;
+   * if the function of which it is a parameter overrides a homograph
+     and has a controlling result, the corresponding parameter in all overridden
+     homographs is also an immutably returnable parameter;
+   * the type of the parameter is tagged, or is a composite type that has a
+     known discriminant part or is not a partial view.
+
+[Note: If we wanted to retain the original "non-*in*" wording, it would go here
+and in the next bulleted list.]
+
+AARM Reason: This is the definition for use in legality rules; these rules only
+apply to types that are composite and cannot become something else.
+
+The part about the homographs ensures that the "immutably returnable parameter"
+property does not change for dispatching calls. In particular, if some tagged type
+does not have an part with an access discriminant, any functions with controlling
+results that it defines will not have returnable parameters. If a descendent
+of that tagged type adds an access discriminant and overrides the function, we
+could dispatch to the function without the special accessibility checking. In
+that case, the function body better not assume that checking was performed, as
+it could allow it to return dangling pointers. We only have to worry about
+controlling results, as in other cases the return type of the dispatching routine
+cannot change.
+
+We need the messy wording excepting partial views as a private type is always
+considered composite and we don't want this to apply to parameters that have private
+types but are really elementary. [Note: For uses in calls, this is not strictly
+necessary, as it only would mean an extra check; but for uses inside of function
+bodies, this is critical so that parameters that were never checked are not used as
+if they were checked. OTOH, it is not certain that you could do anything that
+causes trouble with such a parameter, as it would not be passed by reference and
+would not allow 'Access, nor would it have any visible aliased components. So maybe
+we don't need the messy wording.]
+End AARM Reason.
+
+A "runtime returnable parameter" is a parameter with the following
+characteristics:
+   * it is a parameter of a function with an anonymous access result, or a
+     result subtype whose full view has a part with an access discriminant;
+   * if the function of which it is a parameter overrides a homograph
+     and has a controlling result, the corresponding parameter in all overridden
+     homographs is also a runtime returnable parameter;
+   * the full view of the type of the parameter is a composite type.
+
+AARM Reason: This is the definition for use in dynamic semantic checks. The homograph
+part of the rule is needed for the same reason as notied previously.
+
+
+Add a new Static semantics rule somewhere: For runtime returnable parameters of
+a function call, the master of the expression designating 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].
+[RLB: Not sure where to put this. It really belongs in 7.6.1(3/2), but that seems
+really nasty because how complex the wording there is. We also could put it into 6.4.1,
+after (15) [and the new rule given below] as a "Notwithstanding" rule, but that isn't
+very pleasant, either.]
+
+Add a new Legality Rule after 6.4.1(6):
+
+In a function call, the accessibility level of the actual object
+for each immutably returnable parameter shall not be statically deeper than
+accessibility level of the master of the function result.
+
+AARM Discussion: Since immutably returnable parameters are always composite,
+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).
+
+Add a new Dynamic Semantics after 6.4.1(15): (this would be a new, outer level, bullet)
+
+In a function call, for each runtime returnable 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.
+
+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 subprogram call C, we do not
+intend to require dynamic checks that depend on the master of the actual
+parameters to C, as that would cause nasty distributed overhead (all composite
+parameters would have to carry accessibility levels).
+
+
+The accessibility level of a formal immutably returnable 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). We're talking about inside of the
+function body. This rule would go somewhere in 3.10.2(6-16), I'm not certain
+where.].
+
+[This level needs to be defined statically as it is used in legality rules.
+That prevents using the runtime version for this rule, and that means that
+parameters of untagged private types are not going to be able to
+use this accessibility. That does not appear to be a problem, as such parameters
+do not have any visible aliased parts to return -- discriminants are not aliased;
+the type is private, so it doesn't have any components, and the parameter itself
+is not considered aliased. - RLB]
+
+[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. But this depends on 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 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. - RLB]
+
+
+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;
+    type Accessor_Type (Element : not null 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.]
+A default-initialized object of type Accessor_Type propagates Program_Error.
 
-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.
+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 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.
+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]
+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, 
+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.
+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.
+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
+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? They would look like:
+
+    type RO_Accessor_Type (Element : not null access constant Element_Type) is
+       tagged limited private;
+
+    function RO_Accessor (Position : in Cursor) return RO_Accessor_Type;
+
+    function RO_Accessor (Container : in 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 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 idea is that the accessibility level of "returnable" 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
@@ -144,6 +267,43 @@
 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).
 
+The checks are (mostly) defined dynamically so that they don't break privacy. We want these
+rules to apply to all appropriate functions whose full definitions have the appropriate
+characteristics, but we don't want legality rules depending on the contents of private parts.
+That would cause generic contract problems, as well.
+
+For instance, the properties of the return type are not part of mode-conformance. 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);
+       -- Better be illegal in the body.
+    package Inst2 is new G (Natural, Natural, Some_Func);
+       -- Body is OK.
+
+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.
+
+By making all of the call checks dynamic in cases like this, we avoid the contract problems.
+
 ---
 
 Note that the accessibility of the returned object from a call to Accessor will prevent converting
@@ -160,7 +320,7 @@
       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)) 
+      PAT : AT := new MV.Accessor_Type'(Vect.Accessor (1))
 
       Element : access Element_Type := PAT.Element; -- OK.
 
@@ -179,33 +339,20 @@
 
 ---
 
-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.
+We don't want to make this change for all parameters of function for compatibility
+reasons.
 
-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:
+First, 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.
+
+Second, changing the master of all 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.
 
-    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
@@ -314,46 +461,28 @@
 
 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.
+We could define functions to have returnable parameters only for immutably limited
+result types. That's all we need for the containers, and it would reduce the cases
+where incompatibilities could arise. But it seems to be preventing the use of a useful
+tool.
+
+
+We defined a returnable parameter as any that has a "really" composite type and is
+in a function with an appropriate return type. We could limit these parameters further
+only to cases that have a part that could be usefully returned: a tagged type (which
+is implicitly aliased), a type with an aliased part, or parts with access discriminants.
+We didn't do this as it would cause a maintenance hazard: adding "aliased" to a component
+in a private type could cause existing function calls that use that type (possibly far away)
+to start raising Program_Error. A similar issue would happen if a component with
+an access discriminant were added.
+
+Note that this hazard exists somewhat for functions if the return type is changed. This
+is likely to be fairly rare. It could be mitigated by requiring returning a type with a
+*visible* access discriminant before the function would be considered as having returnable
+parameters. That's all we need to use to implement the container's accessors. But it seems
+restrictive.
 
-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);
-       -- Better be illegal in the body.
-    package Inst2 is new G (Natural, Natural, Some_Func);
-       -- Body is OK.
-
-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
@@ -371,8 +500,8 @@
 
 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.
+renames and for generic matching. That neatly gets rid of generic contract issues and
+all compatibility issues, but of course at the cost of extra syntax.
 
 
 !examples
@@ -828,13 +957,120 @@
 
 ****************************************************************
 
-From: Randy Brukardt
-Sent: Friday, April  10, 2009  6:40 PM
+From: Tucker Taft
+Sent: Sunday, April 26, 2009  10:56 PM
+
+[Tucker was responding to an earlier draft of version /02 of this AI.]
+
+I think what you have proposed is reasonable.  I would suggest some changes
+or perhaps simply a different way of looking at the same essential rules:
 
+  *  Make the access discriminants of the Accessor_Types
+     null excluding.
+
+  *  I don't see the need to say that the finalization
+     of an Accessor_Type has no effect.  I think that is
+     just misleading, especially right after we say that it
+     "needs finalization."  We don't normally define exactly what
+     an implementation must do to finalize a type appearing
+     in a language-defined package.  We merely impose certain
+     requirements (such as no loss of storage) which then
+     imply what needs to occur upon finalization.
+
+  *  If a function call has a result type that, in the caller's view,
+     is anonymous access, classwide, or has a part with an access discriminant
+     ("function call whose result might reference a parameter"), then
+     *all* actual parameters that in the caller's view are tagged, have
+     aliased subcomponents, or have parts with access discriminants
+     ("actual parameters that are referenceable in caller's view"),
+     should be subject to static accessibility checks as part of a
+     legality rule.
+
+  *  If a function call has a result type that, in any view, is anonymous
+     access, classwide, controlling as part of a dispatching call, or
+     has a part with an access discriminant, then *all* actual parameters
+     that are tagged, have aliased subcomponents, or parts with
+     access discriminants in any view, are subject
+     to dynamic accessibility checks prior to the call.
+     (Or, rewording using the phrases used above:
+       If in any view a function call has a result that might reference
+       a parameter, then all actual parameters that are referenceable
+       (in any view) are subject to a dynamic accessibility check before
+       the call.)
+
+     If a referenceable parameter is a "new" object (aggregate or
+     function call), then its master is that of the function call's
+     return object.  If such a call has an actual parameter that
+     is an anonymous allocator, then its master is that of the function
+     call's return object also (I think this partially addresses
+     concerns about the local scope of anonymous allocators).
+     If the call's return object is used to initialize an allocator, and
+     that object is later unchecked-deallocated, then the implementation may
+     at that time also finalize and deallocate these "new" objects
+     associated with the call (including anonymous allocators).
+
+I think we are largely in agreement, and this may be just my attempt to
+reexpress your suggested rules so that I understand them better.
+
 ****************************************************************
 
 From: Randy Brukardt
-Sent: Friday, April  10, 2009  6:40 PM
+Sent: Monday, April  27, 2009  11:31 PM
+
+...
+>   *  I don't see the need to say that the finalization
+>      of an Accessor_Type has no effect.  I think that is
+>      just misleading, especially right after we say that it
+>      "needs finalization."  We don't normally define exactly what
+>      an implementation must do to finalize a type appearing
+>      in a language-defined package.  We merely impose certain
+>      requirements (such as no loss of storage) which then
+>      imply what needs to occur upon finalization.
+
+Understood, but this is a bit of a weird case. All of the wording
+discusses what happens for values that are returned from one of the
+Accessor functions. We need to say *something* about the
+default-initialized case (that is, if one is defined explicitly). In
+particular, it *must not* have any effect on the tampering state of
+some container. Perhaps an AARM Implementation note is enough.
+
+An alternative would be to have a default-initialized object of this type
+raise an exception, since it is a useless construct and represents some
+sort of mistake. That would simplify implementation a bit. I think I'll
+write it up that way, as it addresses your objection.
+
+>   *  If a function call has a result type that, in the caller's view,
+>      is anonymous access, classwide, or has a part with an access discriminant
+>      ("function call whose result might reference a parameter"), then
+>      *all* actual parameters that in the caller's view are tagged, have
+>      aliased subcomponents, or have parts with access discriminants
+>      ("actual parameters that are referenceable in caller's view"),
+>      should be subject to static accessibility checks as part of a
+>      legality rule.
+
+I don't understand the reason for including "classwide" in the result types
+here. That seems like a significant incompatibility with existing code (and
+even Ada 95 code). And you can't dispatch to a routine that has such a
+requirement unless the access discriminant part is known. So it doesn't seem
+like anything could be done with this.
+
+As far as the parameters go, I just applied it to any "really" composite
+parameter, because anything more specific is a maintenance hazard. With the
+rule you have, adding a aliased component to a private type might cause far
+away calls on some unrelated functions that happen to have a parameter of
+the type to start raising Program_Error. Yuck.
+
+>      If such a call has an actual parameter that
+>      is an anonymous allocator, then its master is that of the function
+>      call's return object also (I think this partially addresses
+>      concerns about the local scope of anonymous allocators).
+>      If the call's return object is used to initialize an allocator, and
+>      that object is later unchecked-deallocated, then the implementation may
+>      at that time also finalize and deallocate these "new" objects
+>      associated with the call (including anonymous allocators).
+
+That sounds worse than co-extensions. This level of complexity would kill the
+proposal, and it has nothing whatsoever to do with the problem at hand.
 
 ****************************************************************
 

Questions? Ask the ACAA Technical Agent