!standard 4.6(24.21/4) 20-06-10 AI12-0380-1/03 !standard 6.1.1(0) !standard 13.1.1(17/5) !standard 13.1.1(18/4) !standard H.4(23.2/5) !standard H.4(23.3/5) !standard H.4(23.4/5) !standard H.7(0) !class Amendment 20-06-09 !status work item 20-06-09 !status received 20-06-08 !priority Low !difficulty Medium !subject Fixups for Global annotations !problem We have identified certain issues with the global aspects: 1) We would like to give implementors some freedom in implementing language-defined package specs, including in the area of usage of global variables. Originally we thought we could allow implementors to add "in out synchronized" as needed, and presumed that there would be no impact on users of the package. Alas, that is not so simple if the user of the package is also interested in using Global aspects, because as currently defined, any global variable that a called routine uses must be reflected somehow in the Global aspect of the caller. This clearly can create a situation where a very small change deep within an implementation, to add a cache, for example, or some logging code, could have ripple effects throughout the implementation, and even worse, affect many of the existing clients. 2) A second problem has to do with when parts of a parameter or named global variable are visibly of an access-to-object type. We don't want to limit what can be done with such visible access objects, nor require such effects be included in the global aspects. There is essentially no way a subprogram can know what such access values might designate. The caller might know more, or they might have received such an access value as part of a parameter from their caller. 3) There seems no rule restricting conversions between access-to-subprogram types with different global aspects. 4) In H.7, there seems no single definition of what is a "formal parameter set." 5) In H.7, the Global aspect of an elementary base subtype should be specified as always NULL. 6) The global rules talk about "operations", but not all entities that Global can be applied to are operations, and the connection is vague. Moreover, Ada doesn't even define the term "operations" (it defines some specific sets of operations like "primitive operations" and "predefined operations", but not plain "operations"). Should we try to address these problems? (Yes.) !proposal We propose to address the above problems as follows: 1) Ada has a strong separation between spec and body, and it makes sense to make a similar separation if possible in terms of global variable usage. Some global variable usage is relevant to the caller, and should not be hidden within the implementation. But other global variables are merely for caching, logging, or other caller-irrelevant purposes, and should not need to be reflected in the caller-visible Global aspect. We therefore propose to allow a Private_Global aspect on the body of a program unit, and on the body of a library unit as a default for program units completed within the library unit body, to specify global variable usage that is not relevant to the caller. The syntax would be identical as the Global aspect, and the sets of global variables that are allowed to be read or written by a given program unit would simply be the union of the Global and Private_Global aspect. At a subprogram call (or other invocation of a program unit), only the Global aspect of the invoked routine would be relevant to the caller, and would have to be covered by the union of the caller's Global and Private_Global aspects. Clearly Private_Global aspects should under normal circumstances be limited to globals that are not visible to the callers, and similarly should normally be synchronized, but we do not propose to limit that at this point. Implementations are permittted to enforce such restrictions, but for now we require only a simple capability of separating caller-relevant globals from those that are not of concern to the caller. The intent is that if two calls have the same caller-relevant inputs (i.e. Global variables and in [out] parameters), then they should return the same result in the caller-relevant outputs (i.e. Global variables and [in] out parameters). We allow implementations to detect and complain about such situations Similarly, we allow implementations to complain if two calls that would normally not conflict based strictly on the caller-relevant inputs and outputs would now conflict if the Private_Global aspects are considered. As usual, program execution would be erroneous if an actual data race occurs. 2) To deal with parts of a parameter or a global that are visibly of an access-to-object type, we propose to adopt semantics similar to what we have proposed for generic formal parameters, namely, it is the caller's responsibility to worry about global effects when the called subprogram would have no idea what they might be. Eventually we might allow a subprogram to override the "mode" of the access values that are visible parts of a parameter or global, down from access-to-variable to access-to-constant, or even further down to "never dereferenced," to more precisely indicate how it uses such access-to-object parts, but we leave that capability for a future revision. 3) We propose to limit conversions between access-to-subprogram types where we require the target type has a superset of the global variable sets of the operand type. 4) We propose additional wording to clarify the meaning of "formal parameter set." 5) We propose additional wording to specify the default for the global aspect of a non-derived elementary type to be NULL, in the absence of a predicate. 6) 6.1.1(19/5) attempts to explain the use of "operation" in the rules for Global, but it is really terse and fails to clarify the connection between the operations and the entities on which Global is specified. We try to clarify the wording, and also add a reminder in H.7 about this model. !wording Add after 4.6(24.21/4): If the target type has a Global aspect other than IN OUT ALL or Unspecified, then each mode of the Global aspect of the operand type shall identify a subset of the variables identified by the corresponding mode of the target type Global aspect, or by the IN OUT mode of the target type Global aspect. Replace 6.1.1(19/5): The following is defined in terms of operations; the rules apply to all of the above kinds of entities. with: The following is defined in terms of operations that are performed by or on behalf of an entity. The rules on operations apply to the entity(s) associated with those operations. AARM Discussion: The operations performed by a callable entity are those associated with the body of the entity. For other kinds of entities (such as subtypes, see H.7), we explicitly list the associated operations. Delete 13.1.1(17/5). [AI author's note: this paragraph listed declarative items for which there were no language-defined aspects. This list has become a bit of a maintenance headache, and it did not, as written, have any real semantic content. The actual semantic intent has been combined into (18/4) below.] Modify 13.1.1(18/4): {Unless specified otherwise, a}[A] language-defined aspect shall not be specified in an aspect_specification given on a completion of a [subprogram or generic subprogram] program unit. Modify H.4(23.2-4/5): No_Hidden_Indirect_Globals When within a context where the {union of} applicable [G]{g}lobal aspect{s} is neither Unspecified nor IN OUT ALL, any execution within such a context does neither of the following: * Update a variable that is reachable via a sequence of zero or more dereferences of access-to-object values from a formal parameter of mode IN (after any OVERRIDING -- see H.7), or from a global that is not within the applicable global variable set, or has mode IN{, unless the initial dereference is of a part of the formal parameter or global that is visibly of an access-to-variable type}; * Read a variable that is updatable via a sequence of zero or more dereferences of access-to-object values from a global that is not within the applicable global variable set{, unless the initial dereference is of a part of the formal parameter or global that is visibly of an access-to-object type}. AARM Ramification: The above two rules specify that any {hidden} indirect references are covered by the [G]{g}lobal or formal parameter modes that apply, and {that such references} are *not* subject to alternative paths of access (i.e. aliasing) that could result in conflicts. {On the other hand, any visible access-to-object parts are allowed to designate objects that are accessible via other means, and side-effects on such objects are permitted if the value is visibly of an access-to-variable type. Such effects do not need to be covered by the applicable global aspect(s), but are rather for the caller to worry about.} ... [rest remains the same] Modify H.7(1/5): H.7 Extensions to Global and Global'Class Aspects In addition to the entities specified in 6.1.2, the Global aspect may be specified for a subtype (including a formal subtype), formal package, formal subprogram, and formal object of an anonymous access-to-subprogram type. {The Private_Global aspect may be specified on bodies of program units for which a Global aspect is permitted on their specification. Private_Global follows the same syntax, name resolution, and legality rules as Global.} Modify H.7(2/5): The following additional syntax is provided for specifying Global{, Private_Global,} and Global'Class aspects, to more precisely describe the use of generic formal parameters and dispatching calls within the execution of an operation: Replace H.7/(3/5): extended_global_aspect_definition ::= USE formal_parameter_designator | DO dispatching_operation_specifier Modify H.7(14/5) (Name Resolutuon): The object_name of a dispatching_operation_specifier shall resolve to statically name an object (including possibly a formal parameter) of a tagged class-wide type T'Class{ (or of an access type designating T'Class)}; the dispatching_operation_name of the dispatching_operation_specifier shall resolve to statically denote a dispatching operation associated with T. Modify H.7(15-19/5): (The entire Static Semantics section): Static Semantics The presence of the reserved word OVERRIDING in a global mode indicates that the specification is overriding the mode of a formal parameter with another mode to reflect the overall effect of an invocation of the callable entity on the state associated with the corresponding actual parameter. Redundant[As described in 6.1.1, the following rules are defined in terms of operations that are performed by or on behalf of an entity.] The extended_global_aspect_definition and extended_global_aspect_element can be used to define as part of [the G]{a g}lobal aspect the *formal parameter set* and the *dispatching operation set* used within an operation. The formal parameter set {is the set of generic formal parameters, of any enclosing generic unit (or visible formal package), that might be used within the program unit, directly or indirectly. This set} is identified by a [set]{list} of formal parameter names after the reserved word USE. Alternatively, the reserved word NULL may be used to indicate none of the{se} generic formal parameters, or ALL to indicate all [of the] {such} generic formal parameters, [of any enclosing generic unit (or visible formal package)] might be used within the execution of the operation. If there is no formal parameter set specified for an operation declared within a generic unit, it defaults to USE ALL. {When invoking the corresponding program unit in an instance, the potential global effects of the invocation include those indicated by the Global or Global'Class aspects of the formal parameters identified in the formal parameter set of the program unit.} The dispatching operation set is identified by a set of dispatching_operation_specifiers after the reserved word DO. It indicates that the [G]{g}lobal effects of dispatching calls that *match* one of the specifiers need not be accounted for by other elements of the [G]{g}lobal aspect, but are instead to be accounted for by the invoker of the operation. A dispatching call *matches* a dispatching_operation_specifier if the {name or} prefix of the call statically denotes the same operation(s) as that of the dispatching_operation_specifier, and at least one of the objects controlling the call is denoted by{, or designated by,} a name that statically [denotes]{names} the same object as that denoted by the /object_/name of the dispatching_operation_specifier. In the absence of any dispatching_operation_specifiers, all dispatching calls within the operation are presumed to have the effects determined by the set of Global'Class aspects that apply to the invoked dispatching operation. {AARM To Be Honest: An "object controlling the call" is understood to be any object whose tag is relevant to determining the "controlling tag value" defined in 3.9.2(14-19).} The Global aspect for a subtype identifies the global variables that might be referenced by the following operations of the subtype: default initialization, adjustment as part of assignment, finalization of an object of the subtype, or conversion to the subtype, including the evaluation of any assertion expressions that apply. If not specified for the first subtype of a derived type, the aspect defaults to that of the ancestor subtype; if not specified for a nonderived {composite} first subtype the aspect defaults to that of the enclosing library unit{; for a nonderived elementary first subtype (or scalar base subtype), the aspect defaults to NULL in the absence of a predicate, and to that of the enclosing library unit otherwise}. If not specified for a nonfirst subtype S, the Global aspect defaults to that of the subtype identified in the subtype_indication defining S. The Global'Class aspect may be specified for the first subtype of a tagged type T, indicating an upper bound on the Global aspect of any descendant of T. If not specified, it defaults to Unspecified. {If a Private_Global aspect is specified on the body of a program unit, then within the body of the unit, the relevant global variable sets are each the union of those determined by the Global aspect with those determined by the Private_Global aspect. If a Private_Global aspect is not specified it defaults to that of the enclosing library unit body. If no Private_Global aspect is specified on the enclosing library unit body, then it is as though the Private_Global aspect were specified as NULL, meaning the subprogram is limited to the sets of global variables determined by the Global aspect on the specification.} Modify H.7(22/5): [to clarify what is meant by "the operation"] {Within an operation to which a dispatching_operation_specifier applies, a}[A]ny dispatching call [occurring within the operation] that does not match [a]{the} dispatching_operation_specifier is checked using the Global'Class aspect(s) applicable to the {called} dispatching operation; if there is a match, there is no checking against other elements of the Global aspect(s) applicable at the point of call. Add after H.7(22/5): Implementation Permissions An implementation may restrict the use of global variables identified by a Private_Global aspect applicable to the body of a program unit, if it can determine that the use of such a global variable would produce different results in two distinct invocations of the program unit, in terms of the IN OUT and OUT formal parameters and visible globals, given equivalent inputs in terms of the IN and IN OUT parameters and visible globals, where the /visible globals/ are those identified by an applicable Global aspect. Similarly, the implementation may restrict uses of such global variables if the usage could introduce a conflict with other actions occurring in other logical threads of control, if such a conflict would not otherwise exist. The possible consequences of violating such restrictions are implementation defined, and could include a compile-time error or the raising of Program_Error at run time. !discussion See !problem and !proposal for most of the rationale. The shift of perspective on visible access-to-variable parts was brought about by considerations of "Stable" views and "Implicit_Dereference" types. These both have visible access-to-object parts (discriminants) and in many cases the caller knows what they designate, or at least what is the enclosing container. It is hopeless for the called routine to correctly describe its effects on such objects, without using something like "X.all" which we no longer allow. Nevertheless, in many contexts, the caller knows quite well what object is being updated, and must account for these effects if the caller did not similarly receive the Stable view or Implicit_Dereference object from its caller. Even allowing a subprogram to specify in its Global aspect something like "Param.Disc.all", we would still be stuck with the caller having to know where Param.Disc points. So we are simply saying that if you pass an object with a visible access-to-object part, you need to presume it is going to dereference the corresponding access value. Rather than defining a Bounded Error if Private_Global usage could change the "visible" results, or introduce a conflict with another logical thread of control, we give the implementation permission to restrict such usage, with consequences that match those of Bounded Errors. We chose this approach because formally defining the circumstances for the bounded error is not practical given the difficulty of defining equivalence in inputs and outputs. We leave this area to implementations, while giving them permission to introduce compile-time or run-time restrictions that can provide additional safety guarantees. !example Here are some examples of dispatching_operation_specifiers: type T is tagged private with Input => Stream_Input; procedure Display (X : in T; F : in Ada.Text_IO.File_Type := Ada.Text_IO.Current_Input) with Global => overriding in out F; function Stream_Input (Str : not null access Ada.Streams.Root_Stream_Type'Class) return T with Global => do Read (Str); -- NOTE: requires allowing object_name to denote an object -- that designates a class-wide object -- (discussed above) procedure Fill (X : out T'Class; Str : aliased in out Ada.Streams.Root_Stream_Type'Class) with Global => (in Debug, do T'Input (X), Display (X), Read (Str)); ... procedure Fill (X : out T'Class; Str : aliased in out Ada.Streams.Root_Stream_Type'Class) is begin X := T'Input (Str'Access); if Debug then Display (X); end if; end Fill; !ASIS No ASIS effect. !ACATS test ACATS B-Tests are needed to check that the updated rules are enforced. !appendix ****************************************************************