!standard 3.9.2(2) 05-03-10 AI95-00416/04 !class amendment 05-02-07 !status work item 05-02-07 !status received 05-02-07 !priority High !difficulty Hard !subject Access results, accessibility, and return statements !summary Dispatching on access result is supported, but nulls are allowed. Tasks are activated after returning from a function that returns an object containing tasks. Accessibility levels are defined both for the call site, and at the point of the return statement, which is important for objects with access discriminants and for objects of a class-wide type. !problem We have not fully specified the rules for functions with anonymous access type as a result type. In particular, how do they work with dispatching and what are the accessibility rules. We have also not specified the details for result types with access discriminants, as these have special accessibility considerations. In general, we need to specify the accessibility level of the "return object" given that it might be aliased, have access discriminants, be an object of an anonymous access type, etc. We also need to specify some of the details of what happens if the return object has a controlled or task part. !proposal Dispatching on access result is possible. For example: function Empty_Acc return access T; function Similar(X : access T; Y : access T) return Boolean; ... M : access T'Class := ... B : Boolean := Similar(M, Empty_Acc); The tag of M.all determines which overriding of Empty_Acc is invoked. A call on a function with a controlling access result may be dereferenced and still be a dynamically tagged or tag indeterminate expression. Defaults for controlling access parameters should be permitted, so long as they are functions with controlling access results. --------------------- Caller does *not* pass in an accessibility level for return objects. Return object has accessibility between the uplevels of the function and the return statement. Hence: Ok for return object to point to up-levels Ok for locals of return statement to point to return object Not Ok for locals outside return statement to point to return object Not Ok for up-levels to point to return object Not Ok for return object to point to locals Function call has accessibility level of function decl, so may *not* use call on local function to initialize object in longer-lived heap if it has an access discrim or is of a class-wide type. If access discriminants are initialized individually, then type conversion provides for the accessibility checks. However, if the access discriminants are not specified individually, but rather come from a named constrained subtype, or from an initializing object, then there is no type conversion involved, and hence we need an explicit rule for the needed accessibility check. This is relevant at a function return and at an allocator. At a function return, we can be returning a record aggregate (access discriminants given individually), an extension aggregate (access discriminants could come from ancestor part, either from an object or named constrained subtype, or from individual values specified after the "with"), a part of an existing object, a part of a function call (level based on level of function decl). At an allocator we have all of these possibilities, plus we have an unitialized allocator where the subtype_indication denotes a named constrained subtype, or an uninitialized allocator where the subtype_indication contains an explicit constraint (access discriminants specified individually). --------------------- Tasks are not activated until outermost enclosing object fully and successfully initialized, and certainly not prior to return, since only caller knows extent of outermost enclosing object. A return object that is not successfully initialized, is finalized prior to exiting the return statement. !wording Change first sentence of 3.2.3(1): An operation operates on a type T if it yields a value of type T, if it has an operand whose expected type (see 8.6) is T, or if it has an access parameter {or access result type} (see 6.1) designating T. Change 3.7(27/2) to: For an access discriminant, an access_definition is elaborated when the value of the corresponding access discriminant is defined: by evaluation of its default_expression, by elaboration of a discriminant_constraint, or by an assignment that initializes the enclosing object. Add to end of 3.9.2(2/2): {Similarly, if the call is to a function with access result type designating T, then the call has a *controlling access result*, and the context can similarly control dispatching.} Change 3.9.2(4): ... if it is a call with a controlling result {or controlling access result}, ... Change 3.9.2(5): ... or it is a call with a controlling result {or controlling access result} and ... Change 3.9.2(6): ... if it is a call with a controlling result {or controlling access result}, ... Delete the second sentence of 3.9.2(11): The default_expression for a controlling formal parameter of a dispatching operation shall be tag indeterminate. [A controlling formal parameter that is an access parameter shall not have a default_expression.] Delete 3.9.2(11.b) (it explained the second sentence of 3.9.2(11)). Change 3.9.2(18/2): * If the call has a controlling result {or controlling access result} and is itself{, or designates,} a [(possibly parenthesized or qualified)] controlling operand of an enclosing call on a dispatching operation of a descendant of type T, then its controlling tag value is determined by the controlling tag value of this enclosing call; Change 3.9.2(18.1/2): * If the call has a controlling result {or controlling access result} and {(possibly parenthesized, qualified, or dereferenced)} is the [(possibly parenthesized or qualified)] expression of an assignment_statement whose target is of a class-wide type, then its controlling tag value is determined by the target; Add after 3.10(12): AARM NOTE: Note that we considered imposing a similar implicit null exclusion for controlling access results, but chose not to do that, because there is no Ada95 compatibility issue, and there is no automatic null check inherent in the use of a controlling access result. If a null check is necessary, it is because there is a dereference of the result. If there is no dereference of the result, a null return value is perfectly acceptable, and can be a useful indication of a particular status of the call. Add after 3.10.2(7/2): AARM NOTE: This rule defines the accessibility of all named access types, as well as the accessibility level of all anonymous access types other than those for access parameters and access discriminants. Special rules exist for the accessibility level of such anonymous types. Components, stand-alone objects, and function results whose (anonymous) type is defined by an access_definition have accessibility levels corresponding to named access types defined at the same point. Ramification: {AI95-00230-01} Because accessibility level is determined by where the access_definition is elaborated, for a type extension, the anonymous access types of components (other than access discriminants) inherited from the parent have the same accessibility as they did in the parent; those in the extension part have the accessibility determined by the scope where the type extension is declared. Similarly, the types of the non-discriminant access components of a derived untagged type have the same accessibility as they did in the parent. Change 3.10.2(9) as follows: * The accessibility level of a view conversion{, qualified expression, or parenthesized_expression,} is the same as that of the operand. Replace 3.10.2(10/2) with: * The accessibility level of the result of a function call that is used as a prefix of a name or as the actual parameter in a call is that of the immediately enclosing master. In other contexts, the accessibility level is that of the object being initialized from the function result. Add after 3.10.2(10/2): * Within a return statement, the accessibility level of the return object is that of the execution of the return statement. If the return statement completes normally by returning from the function, prior to leaving the function, the accessibility level of the return object changes to be a level determined by the point of call. AARM Note: We define the accessibility level of the return object during the return statement to be that of the return statement itself so that the object may be designated by objects local to the return statement, but not by objects outside the return statement. In addition, the intent is that the return object gets finalized if the return statement ends without actually returning (for example, due to propagating an exception, or a goto). For a normal return, of course, no finalization is done before returning. Delete 3.10.2(11.1/2) (it is redundant). Change 3.10.2(12/2) to be: * For an access discriminant, the accessibility level of its anonymous access type is determined as follows: + For an access discriminant whose value is determined by a discriminant_association in a subtype_indication, the accessibility level of the object or subprogram designated by the associated value (or library level if the value is null); AARM NOTE: This deals with the following cases: - Extension aggregate where ancestor part is a subtype_mark denoting a constrained subtype; - Uninitialized allocator where subtype_indication defines a constrained subtype; - Discriminant of object with a constrained nominal subtype, including constrained components, the result of calling a function with a constrained result subtype, the dereference of an access-to-constrained subtype, etc. + For an access discriminant of an object defined by an aggregate where the value of the discriminant is determined by a component_association in the aggregate, the accessibility level of the object or subprogram designated by the associated value (or library level if the value is null); + For an access discriminant of any other object with an unconstrained nominal subtype, the accessibility level of the object. AARM NOTE: In other words, if you know the value of the discriminant from a discriminant constraint or an aggregate component association, then that determines the accessibility level; if you don't know it, then it is based on the object itself. Delete 3.10.2(12.a/2) (it was moved up to be after paragraph 7/2). Delete 3.10.2(13.2/2) (it is redundant). Change 3.10.2(14): * The accessibility level of an object created by an allocator is the same as that of the access type, except for an allocator that defines the value of an access parameter or an access discriminant. For an access parameter, the accessibility level is that of the master immediately enclosing the call. For an access discriminant, the accessibility level is determined as follows: + for an allocator used to define the constraint in a subtype_declaration, the level of the subtype_declaration; + for an allocator used to define the constraint in a component_definition, the level of the enclosing type; + for an allocator used to define the discriminant of an object, the level of the object. Add after 4.8(5.1/2): If the designated subtype of the type of the allocator has one or more unconstrained access discriminants, then the accessibility level of the anonymous access type of each access discriminant, as determined by the subtype_indication or qualified_expression of the allocator, shall not be statically deeper than that of the type of the allocator (see 3.10.2). Change 4.8(7/2): ... is not deeper than that of the type of the allocator. {If the designated subtype of the allocator has one or more unconstrained access discriminants, then a check is made that the accessibility level of the anonymous access type of each access discriminant is not deeper than that of the type of the allocator.} Program_Error is raised if [this] {either such} check fails. Add after 6.5(5.3/2): If the result type is class-wide, the accessibility level of the type of the return_expression shall not be statically deeper than that of the master that elaborated the function body. If the result subtype has one or more unconstrained access discriminants, the accessibility level of the anonymous access type of each access discriminant, as determined by the return_subtype_indication or the return expression, shall not be statically deeper than that of the master that elaborated the function body. AARM NOTE: We know that if the result type is class wide, then there must be a return expression. Similarly, if the result subtype is unconstrained, then either the return_subtype_indication (if any) is constrained, or there must be a return expression. Add after 6.5(5.5/2): AARM NOTE: If the result type is controlled or has a controlled part, appropriate calls on Initialize or Adjust are performed prior to executing the handled_sequence_of_statements, except when the initial expression is an aggregate (which requires build-in-place with no call on Adjust). If the return statement exits without resulting in a return (for example, due to an exception propagated from the return expression or the handled sequence of statements, or a goto out of the handled sequence of statements), the return object is finalized prior to leaving the return statement. Change 6.5(6/2): For the execution of a {simple_}return_statement, ... Add after 6.5(6/2): If the return object has any parts that are tasks, the activation of those tasks does not occur until after the function returns (see 9.2). AARM NOTE: Only the caller can know when task activations should take place, as it depends on the context of the call. If the function is being used to initialize the component of some larger object, then that entire object must be initialized before any task activations. Even after the outer object is fully initialized, task activations are still postponed until the "begin" at the end of the declarative part if the function is being used to initialize part of a declared object. Change 6.5(20.1/2): If the result type is class-wide, a check is made that the accessibility level of the type identified by the tag of the result is not deeper than that of the master that elaborated the function body. {If the result subtype has one or more unconstrained access discriminants, a check is made that the accessibility level of the anonymous access type of each access discriminant, as determined by the return_subtype_indication or return expression, is not deeper than that of the master that elaborated the function body.} If [this] {either} check fails, Program_Error is raised. Add after 6.5(22/2): Implementation Permission If the result subtype of a function is unconstrained, and a call on the function is used to provide the initial value of an object with a constrained nominal subtype, Constraint_Error may be raised at the point of the call while elaborating a return_subtype_indication or evaluating a return expression within the function, if it is determined that the value of the result will violate the constraint of this object's subtype. AARM NOTE: Without such a permission, it would be very difficult to implement "build-in-place" semantics. Such an exception is not handleable within the function, because in the return-by-copy case, the constraint check to verify that the result satisfies the constraints of the object being initialized happens after the function returns, and we want the semantics to change as little as possible when switching between return-by-copy and build-in-place. This implies further that upon detecting such a situation, the implementation may need to simulate a goto to a point outside any local exception handlers prior to raising the exception. Change 7.6.1(3/2): ... except in the case of a master: the execution of a body other than a package_body; the elaboration of a declaration other than the declaration of a package {or of a type}; the execution of [an accept_statement, a block_statement, or a simple_statement] {a statement}; Change 9.2(2): A task object (which represents one task) can be {a part of a stand-alone object, of an object created by an allocator, or of an anonymous object of a limited type} [created either as part of the elaboration of an object_declaration occurring immediately within some declarative region, or as part of the evaluation of an allocator]. All tasks {that are part of any of the stand-alone objects} created by the elaboration of object_declarations {(or generic_associations of formal objects of mode IN)} of a single declarative region [(including subcomponents of the declared objects)] are activated together. [Similarly, all tasks created by the evaluation of a single allocator are activated together. The activation of a task is associated with the innermost allocator or object_declaration that is responsible for its creation.] {All tasks that are part of a single object that is not a stand-alone object are activated together.} Change 9.2(3): For {the} tasks [created by the elaboration of object_declarations] of a given declarative region, the activations are initiated within the context of the handled_sequence_of_statements ... Change 9.2(4): {For tasks that are part a single object that is not a stand-alone object, activations are initiated after completing any initialization of the outermost object enclosing these tasks, prior to performing any other operation on the outermost object. In particular, for} [For] tasks {that are part of the object} created by the evaluation of an allocator, the activations are initiated as the last step of evaluating the allocator, [after completing any initialization for the object created by the allocator, and] prior to returning the new access value. {For tasks that are part of an object that is the result of a function call, the activations are not initiated until after the function returns.} AARM NOTE: The intent is that "temporary" objects with task parts are treated similarly to an object created by an allocator. The "whole" object is initialized, and then all of the task parts are activated together. Each such "whole" object has its own task activation sequence, involving the activating task being suspended until all the new tasks complete their activation. !discussion Our intent is to define the checks needed when an object with access discriminants is initialized by assignment as equivalent to converting each access discriminant to the type of the access discriminant of the target. When a function call is used as a prefix (or as an actual parameter), such as for taking 'Access or selecting a component, it is considered a temporary object, and the accessibility level of an access discriminant subcomponent will have the level of the nearest enclosing master. On the other hand, if the function call is used as a "whole" to initialize an allocator, a declared object, or a return object, it takes on the accessibility level of the function declaration. We want to preserve the model that an allocator (of an anonymous type) that is used to initialize an access discriminant results in an allocated object that has the same storage pool and lifetime as the object with the access discriminant. This should be true whether the enclosing object is limited or non-limited. For a limited function return, the caller has to control where the return object is built. If the result subtype is unconstrained, while the call is being used to initialize a constrained object, including a component, the space available to the object is bounded. To avoid overrunning this space, the caller must pass in some sort of implicit parameters which will allow the return statement to determine the limits on the space it may occupy. One possibility is to pass in the values for the discriminants, or perhaps to preeinitialize them in the space for the return object. There should be an implementation permission to allow raising Constraint_Error at the beginning of the return statement, prior to fully evaluating the return expression or executing the handled sequence of statements. Something analogous to a 'Constrained bit can be provided, to indicate whether the discriminant values are preinitialized. ----------------- Our last discussion about access discriminants and allocators in Paris was a bit concerning, I'm sure. For what it's worth, I believe I have worked out all the problems, and the "joined at the hip" model will work for nonlimited types as well. Generally, the solution is to return more closely to the rule that an access discriminant always has the same level as the object it is a component of. I had broken that rule for objects returned by functions when referring to an access discriminant of a subcomponent of the result, and that was causing the trouble, I believe. Calling a function and then only using a part of the result is quite rare, and using a part that happens to have an access discriminant that is initialized from the enclosing object's access discriminant is well below the noise level. Overall, the general rule becomes that as soon as you select a component of a function result, the function result is treated as a "temp", and it takes on a very local accessibility level, as do any access discriminants it has. E.g.: X : T := func().component; is not permitted if component has an access discriminant initialized from the enclosing object, because the call is a temp, and we are trying to initialize a longer-lasting object. Another way to think about it is that only when "build-in-place" is possible can the result of the function call be long-lived. !example (See umm, er...imagine one here. - ED) --!corrigendum 02.09(2) !ACATS test !appendix *************************************************************