CVS difference for ai05s/ai05-0050-1.txt

Differences between 1.4 and version 1.5
Log of other versions for file ai05s/ai05-0050-1.txt

--- ai05s/ai05-0050-1.txt	2008/10/16 01:00:25	1.4
+++ ai05s/ai05-0050-1.txt	2008/10/16 01:40:40	1.5
@@ -1,4 +1,4 @@
-!standard 6.5(24/2)                              08-10-10    AI05-0050-1/04
+!standard 6.5(24/2)                              08-10-15    AI05-0050-1/05
 !class binding interpretation 07-05-04
 !status work item 07-05-04
 !status received 07-04-20
@@ -70,7 +70,29 @@
   These permissions apply transitively in the case of a function call
   which returns the value of another function call.
 
+  These permissions do not apply in the case of an extended
+  return object with mutable discriminants. That's necessary because in that
+  case a return object can be created with the "wrong" discriminants and
+  then changed to the "right" discriminants later (but before returning).
+  We don't want this case raising an exception when the canonical semantics
+  will not do so.
+
+  It's still possible to write a program that will raise an exception using
+  this permission that would not in the canonical semantics. That could happen
+  if a return statement with the "wrong" discriminants or bounds is abandoned (via
+  an exception, or for an extended_return_statement, via an exit or goto statement),
+  and then a return statement with the "right" discrimiants or bounds is executed.
+  The only solution for this problem is not have the permission at all, but this is
+  too unusual of a case to worry about the effects of the permission, especially
+  given the implementation difficulties for build-in-place objects that this
+  permission is intended to ease.
+
+  Note that the mutable-discriminant case only happens when
+  build-in-place initialization is optional. This means that any difficulties
+  associated with implementing build-in-place initialization without these
+  permissions can be sidestepped by not building-in-place.
 
+
 !discussion
 
 The implementation permissions of 6.5(24/2) do not go far enough.
@@ -86,17 +108,13 @@
   of the subtype of this object.
   
 This is a fine start, but more is needed in order to efficiently
-implement initialization in place as required by AI95-00318-02.
-
---------
+implement build-in-place initialization as required by AI95-00318-02.
 
-Issue #1:
+First, the original permission does not cover a function with a constrained
+result type. But such a function can be used to initialize an object
+with the wrong constraints, and thus has the same implementation issues
+that the original permission is intended to eliminate.
 
-If an object is being initialized by a function call and the implementation
-can tell before the call that the object subtype and the function result
-subtype are incompatible, then the implementation should be permitted
-to raise an exception before the call.
-
 Consider:
 
   declare
@@ -110,8 +128,8 @@
 
     type Unconstrained is array (Positive range <>) of T;
     
-    subtype S1 is Unconstrained (1 .. Ident_Int (10));
-    subtype S2 is Unconstrained (1 .. Ident_Int (11));
+    subtype S1 is Unconstrained (1 .. Ident_Int (20));
+    subtype S2 is Unconstrained (1 .. Ident_Int (10));
 
     function F return S1 is
     begin
@@ -123,42 +141,18 @@
   begin null; end;
 
 An implementation should be allowed to raise Constraint_Error before the
-call to F (without invoking 11.6).
-
-In the case where the result subtype of a function is a constrained definite
-structure, an implemention ought to be able to have a caller
-simply pass in a buffer address; nothing more complicated than that should
-be needed.
-
-In order to implement initialization in place, F must be passed the address of
-X. An implementation must verify before the call that S1 and S2 have compatible
-constraints - it would not do to pass in, for example, the address of a small
-object to a function which is going to fill in a large result.
+call to F (without invoking 11.6). Otherwise, F could try to build a large
+object in place in a smaller object. That would not be good.
 
-The question is what to do if that test fails. The simple thing would be to
-raise Constraint_Error at that point. The annoyingly complicated thing that
-the language currently seems to require is to allocate a buffer of the size
-the function expects, pass that buffer address into the function, finalize the
-returned value, and then raise Constraint_Error (or something like that).
+Thus the permission was extended to cover all types of function returns.
 
-One could go further and handle cases involving qualified expressions
-such as
-
-    Y : S1 := S2'(F);
-
-but this doesn't seem necessary.
-
---------
-
-Issue #2:
-
-Initialization in place is only required for limited types, so it would
+Second, build-in-place is only required for limited types, so it would
 be ok for an implementation to ignore the implementation permissions of
-6.5(24/2) for a nonlimited type. If an implementation chooses not to
-do this, however, there can be problems with extended return statements
-which attempt to modify discriminants of the function result object after
-it has been initialized.
+6.5(24/2) for a nonlimited type. If an implementation chooses to implement
+build-in-place for nonlimited types, it must take care with types with
+mutable discriminants.
 
+In particular, it must not modify discriminants of constrained objects.
 Consider:
 
   subtype Index is Integer range 1 .. 20;
@@ -177,32 +171,52 @@
   end F;
 
   X1 : Rec (10) := F;
-
-For an in-out or out mode parameter with an unconstrained nominal subtype, 
-the formal inherits the constraints (if any) of the actual. The same
-model should apply, at the discretion of the implementation, to return objects
-in the case where the nominal subtype of the return object is unconstrained
-and definite and "a call on the function is  used to provide the initial value
-of an object with a constrained nominal subtype".
-
-This means that for the preceding example, execution of F could (at the
-discretion of the implementation) be abandoned (and Constraint_Error raised at
-the call site) at the point of the assignment statement. If the literal
-"10" in the declaration of X1 were replaced with some other value, then the
-default initialization of Result could raise Constraint_Error.
-
-There is also a similar issue in the case of a function call which is
-used to initialize an allocator when the designated subtype of the
-access type is unconstrained but allocated objects themselves are constrained.
 
--------
+In a case like this, the revised permission does not provide a permission to
+raise Constraint_Error early (the return object is not "known to be
+constrained", and thus the second bullet does not apply). That means that
+implementations will typically have to use an anonymous temporary object
+in this case and avoid building the result in place.
+
+We considered a model which would be similar to an in-out or out mode parameter
+with an unconstrained nominal subtype. In this model, Constraint_Error could be
+raised anywhere that the return object was initialized or assigned, not just at
+the point of the call.
+
+Besides seeming rather non-specific, this model would allow an exception to be
+raised in cases where it would not be raised in the absence of the permission.
+That seems bad. For instance:
 
-Issue #3:
+    declare
+      type T (Is_Big : Boolean := True) is
+       record
+         case Is_Big is
+           when False => null;
+           when True  => Big_Component : String (1..1024) := (others => 'X');
+         end case;
+       end record;
+       
+      function F return T is
+      begin
+        return X : T do -- X is unconstrained
+         X.Big_Component := (others => 'Y');
+         X := (Is_Big => False);
+        end return;
+      end F;
+      
+      Small_Object : T (Is_Big => False) := F;
+    begin
+      null;
+    end;
+ 
+The permissive model would have allowed the declaration of X to raise
+Constraint_Error. But that is bad, as the function ultimately returns an
+object with the correct discriminant. As such, we did not use the
+permissive model.
 
-The implementation permissions of 6.5(24/2) should apply recursively in
+Finally, the implementation permissions of 6.5(24/2) should apply recursively in
 the case where a function call returns the result of another function call.
-
-Consider:
+For instance:
 
   declare
     type T;
@@ -233,50 +247,16 @@
     X : Unconstrained (1..3) := F2;
   begin null; end;
   
-
 In the execution of this example, it should be ok for the return statement
 in F1 to raise Constraint_Error before executing any calls to Some_Function.
 
-One might argue that this freedom is already implicit in the current wording,
-but this does not seem to be obvious.
-
---------
-
-It seems that it would not make sense to propose RM wording for these issues
-until we reach consensus on which, if any, of these issues require
-any action at all.
-
-One could argue that no action is required for any them:
-   #1 is essentially a complaint that the language requires an
-      implementation to do something that is silly and complex, but
-      not something that is undefined or impossible.
-   #2 is only a concern for implementations which choose to implement
-      initialization in place for functions with unconstrained definite
-      non-limited discriminated result subtypes. The language only
-      requires initialization in place in the limited case.
-      If an implementation doesn't want to deal with the complications of
-      implementing the given example, then maybe it shouldn't choose to
-      implement initialization in place for this case.
-   #3 could be viewed as requiring only a clarification of the current
-      wording.
-
 ----
 
-It has been argued that these permissions are already implied by
+One could argue that these permissions are already implied by
 11.6 and therefore do not need to be repeated. This is simply not the case.
 For example, the permissions given in this section allow a constraint check
 to be moved into an independent subprogram. 11.6 alone would not allow this.
 
-This is necessary to support allocation of function result objects by
-compiler-generated callback routines, where the caller of a function
-passes in a reference to a storage allocation routine which
-is invoked when the constraints of the function result are known.
-If a constraint check performed by the callback routine fails, this
-appears to the user as though Constraint_Error were raised at the point
-where the callback routine was called (i.e., within the function).
-If the function in question is an independent subprogram, this means
-that the constraint check has been moved into an independent subprogram.
-
 Even if the permissions of this section were redundant, they should still
 be stated explicitly here for the sake of the clarity of the language
 definition. Reliance on these permissions is effectively required in the cases
@@ -289,132 +269,43 @@
 ----
 
 The original wording of 6.5(24/2) applied in the case of a scalar result
-type. This seems wrong. Do we really want this paragraph to apply in the
-case of a function whose result subtype is Standard.Float? No.
-That is why the revised wording includes the phrase "of a composite type".
+type. We don't want this permission to apply (for example) in the case
+of a function whose result subtype is Standard.Float. Thus, the revised
+The revised permission includes the phrase "of a composite type".
 This permission refers to dynamic semantics, so there are no issues with
 privacy.
 
 ----
-
-
-With the revised wording, in the following example,
-  
-    declare   
-      type T is
-	limited record
-	    ... ;
-	end record;
-       -- requires in-place initialization
-
-      type Unconstrained is array (Positive range <>) of T;
-    
-      function Some_Function return T is ... ;
-
-      function F1 return Unconstrained is
-      begin
-        return (1 .. 10 => Some_Function);
-      end F1;
-
-      function F2 return Unconstrained is
-      begin
-        return F1;
-      end F2;
-
-      X : Unconstrained (1..3) := F2;
-    begin
-      null;
-    end;
-      
-Constraint_Error may be raised in F1 before any calls to Some_Function
-are executed.
-  
-Similarly:
-  
-    declare
-      type T is
-	limited record
-	    ... ;
-	end record;
-       -- requires in-place initialization
-
-      type Unconstrained is array (Positive range <>) of T;
-      
-      subtype Constrained is T (1 .. 3);
-      
-      function Some_Function return T is ... ;
-
-      function F1 return Unconstrained is
-        begin return (1.. 10 => Some_Function); end;
-      
-      function F2 return Constrained is begin return F1; end if;
-      
-      function F3 return Unconstrained is
-      begin
-        return Constrained'(F1);
-      end F3;
-      
-      
-    begin
-      begin
-        declare
-          X : Unconstrained := F2;
-        begin null; end;
-      exception when Constraint_Error => null;
-      end;
-      
-      begin
-        declare
-          X : Unconstrained := F3;
-        begin null; end;
-      exception when Constraint_Error => null;
-      end;
-      
-      begin
-        declare
-          X : Unconstrained := Constrained'(F1);
-        begin null; end;
-      exception when Constraint_Error => null;
-      end;      
-    end;
-    
-In all three of these cases, Constraint_Error may be raised in F1 before
-the execution of any calls to Some_Function.
-  
-Note that these permissions do not apply in the
-case of an extended return object with mutable discriminants.
-For example:
   
-    declare
-      type T (Is_Big : Boolean := True) is
-       record
-         case Is_Big is
-           when False => null;
-           when True  => Big_Component : String (1..1024) := (others => 'X');
-         end case;
-       end record;
-       
-      function F return T is
-      begin
-        return X : T do -- X is unconstrained
-         X.Big_Component := (others => 'Y');
-         X := (Is_Big => False);
-        end return;
-      end F;
-      
-      Small_Object : T (Is_Big => False) := F;
-    begin
-      null;
-    end;
-      
-and so Constraint_Error must not be raised (which is good).
-   
-Note that this mutable-discriminant case only happens when
-in-place initialization is optional. This means that any difficulties
-associated with implementing in-place initialization without these
-permissions can be sidestepped.
-
-
+It is still possible for an implementation taking advantage of these permissions
+to raise Constraint_Error in cases were the canonical semantics would not have.
+That can happen when a return statement with the "wrong" discriminants is
+abandoned and then a return statement with the "right" discriminants is executed
+in its place. For instance:
+
+     type Rec (D : Integer) is record ...
+
+     function F return Rec is
+     begin
+         return O : Rec(4) do -- (1)
+            if <some condition> then goto Different_Object; end if;
+         end return;
+        <<Different_Object>>
+         return O : Rec(5) do -- (2)
+            ...
+         end return;
+     end F;
+
+     O : Rec(5) := F;
+
+The permission above would allow raising Constraint_Error at (1). But since that
+object is never actually returned, and the object that is returned is the right
+shape, the full function call which did not take advantage of the permission
+would not raise any exception.
+
+This cannot be avoided without dropping the permission completely; but that would
+have an unacceptable implementation cost for build-in-place objects. (Rec in the
+example above could be a type that requires build-in-place.)
 
 !ACATS test
 

Questions? Ask the ACAA Technical Agent