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

Differences between 1.7 and version 1.8
Log of other versions for file ai05s/ai05-0142-4.txt

--- ai05s/ai05-0142-4.txt	2010/04/17 04:50:16	1.7
+++ ai05s/ai05-0142-4.txt	2010/04/29 06:27:42	1.8
@@ -8,12 +8,13 @@
 !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 work item 09-05-17
 !status received 09-05-17
 !priority Medium
 !difficulty Medium
-!subject Explicitly aliased parameters and accessors for Ada.Containers
+!subject Explicitly aliased parameters
 
 !summary
 
@@ -50,6 +51,9 @@
 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):
@@ -106,8 +110,8 @@
 
 Language Design Principles
 For explictly 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 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.
@@ -213,93 +217,6 @@
 
 [Editor's note: We're deleting the parenthetical remark, as it is no longer true by itself.]
 
-
-(Note: The following requires AI05-0143-1 to be approved.)
-Add the following to each Ada.Containers package immediately after
-Update_Element (with suitable substitutions for the names of types).
-
-    type Constant_Reference_Type (Element : not null access constant Element_Type) is
-        tagged limited private;
-
-    type Reference_Type (Element : not null access Element_Type) is
-        tagged limited private;
-
-[Bob recommends just using "E" for the name of the discriminant.]
-
-Constant_Reference_Type and Reference_Type need finalization.
-
-The default initialization of an object of type Constant_Reference_Type or Reference_Type
-propagates Program_Error.
-
-AARM Reason: It is expected that Reference_Type (and Constant_Reference_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 Constant_Reference (Container : aliased in Vector; Position : in Cursor)
-       return Constant_Reference_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, Constant_Reference returns an object whose discriminant is an access
-value that designates the element designated by Position. Program_Error is propagated
-if any operation tampers with the elements of Container while the object returned
-by Constant_Reference exists and has not been finalized.
-
-    function Reference (Container : aliased in out Vector; Position : in Cursor)
-       return Reference_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, Reference returns an object whose discriminant is an access value
-that designates the element designated by Position. Program_Error is propagated if
-any operation tampers with the elements of Container while the object returned
-by Reference 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 Constant_Reference (Container : aliased in Vector; Index : in Index_Type)
-       return Constant_Reference_Type;
-
-If Index is not in the range First_Index (Container) .. Last_Index (Container),
-then Constraint_Error is propagated. Otherwise, Constant_Reference returns an object
-whose discriminant is an access value that designates the element at position Index.
-Program_Error is propagated if any operation tampers with the elements of Container
-while the object returned by Constant_Reference exists and has not been finalized.
-
-    function Reference (Container : aliased in out Vector; Index : in Index_Type)
-       return Reference_Type;
-
-If Index is not in the range First_Index (Container) .. Last_Index (Container),
-then Constraint_Error is propagated. Otherwise, Reference returns an object whose
-discriminant is an access value that designates the element at position Index.
-Program_Error is propagated if any operation tampers with the elements of Container
-while the object returned by Reference 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
-Reference or Constant_Reference is finalized before the result object returned
-by the call to Reference or Constant_Reference is finalized.
-
-AARM Reason: Each object of Reference_Type and Constant_Reference_Type probably
-contains some reference to the originating container. If that container is prematurely
-finalized (which is only possible via Unchecked_Deallocation, as accessibility checks
-prevent passing a container to Reference that will not live as long as the result), the
-finalization of the object of Reference_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 explicitly aliased parameters of functions is defined
@@ -316,40 +233,6 @@
 
 ---
 
-Note that the accessibility of the returned object from a call to Reference 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 (thus
-we can tie the tampering check to the lifetime of the returned object).
-
-It is possible to use Unchecked_Deallocation to destroy the returned Reference_Type object
-prematurely, while continuing to use the returned access. The accessibility check on the container
-argument to the Reference 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 AR is access MV.Reference_Type;
-      procedure Free is new Ada.Unchecked_Deallocation (MV.Reference_Type, AR);
-
-      PAR : AR := new MV.Reference_Type'(Vect.Reference (1))
-
-      Element : access Element_Type := PAR.Element; -- OK.
-
-   begin
-      Free (PAR); -- Tampering checking ends here!
-
-      Vect.Delete (1); -- No check here.
-      ... Element.all ... -- 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.
-
----
-
 We don't want to treat all parameters of functions like explicitly aliased parameters for
 compatibility and wording reasons.
 
@@ -557,189 +440,10 @@
 for this case. (And it would be weird to have them, given that regular parameter passing
 does not try to prevent this problem.)
 
----
-
-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 Reference (C : aliased 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; -- OK, NT is constrained.
-    begin
-       ...
-       Far_Away.Some_Complex_Operation (...);
-       if Component > 10 then -- ** Oh-oh!! Component has moved.
-          ...
-       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)); -- Element 1.
-      Data.Node_List.Append (New_Item => (D => True, CT => 4)); -- Element 2.
-      Data.Node_List.Append (New_Item => (D => False, CF => 26.5)); -- Element 3.
-      ...
-      Process.Process_True (Data.Node_List.Reference (2).all);
-      ...
-  end Main;
-
-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.
-
 !examples
-
-The new Reference 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.Reference(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 Reference is limited and (probably) controlled is not
-visible in the code as the function call is used directly as a prefix.
-
-You could also save the entire object as long as needed:
-
-   declare
-      My_IV : Nested_Vectors.Reference_Type := NV.Reference(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
-discriminant access value:
-
-   declare
-      My_IV : Integer_Vectors.Vector renames NV.Reference(0).Element.all;
-   begin
-
-Even in this case, the wrapping (probably controlled) 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.Reference(0).Element; -- Illegal!
-   begin
-
-This is illegal because the accessibility check fails. The (anonymous) object
-returned here will have the master of the expression (as a prefix, the special
-"directly initializing" rule does not apply to this function call), 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.Reference(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.Reference(0).Element);
-
-or
-
-    Munge (NV.Reference(0).Element.all);
 
+See AI05-0212-1 for how this feature is defined and used in the Containers libraries,
+including examples of use.
 
 !ACATS test
 

Questions? Ask the ACAA Technical Agent