!standard 12.03 (12) 04-04-02 AI95-00359-02/01 !standard 12.03 (20) !standard 13.14 (05) !class amendment 04-04-02 !status work item 04-04-02 !status received 04-04-02 !priority Low !difficulty Medium !subject Deferring Freezing of a Generic Instantiation !summary To better support generic signature packages, and mutually dependent types that involve generic instantiations, it is proposed that a generic instantiation not cause freezing immediately if the generic is preelaborated. For such an instantiation, no special freezing happens at the point of the instantiation, other than the freezing that is due to the elaboration of the declarations within the specification of the instantiation. Elaboration of the body of the instantiation is deferred until the first use of a two-part entity exported by the generic, or until a "general" freezing point, whichever occurs first. An instantiation is illegal if its body would need to be elaborated at a place where the actual parameters are not all completely defined. NOTE: This proposal is guaranteed to be upward compatible: the place where the body of an instance gets elaborated may change, but the elaboration of a preelaborated unit has no observable effect, so that's not a problem. All existing instantiations are legal, because they freeze their actual parameters, so surely these parameters are completely defined. !problem Ada 95 provides formal package parameters. One way of using these parameters is to define a "signature" for a class of abstractions, such as all set abstractions, or all physical unit abstractions, and then build a new generic abstraction on top of the original class of abstractions using this signature as the template for a formal package parameter. Unfortunately, it is difficult to use signatures because of the fact that an instantiation freezes all of its actual parameters. For example: Given the signature for a set abstraction: generic type Element is private; type Set is private; with function Size(Of_Set : Set) return Natural is <>; with function Union(Left, Right : Set) return Set is <>; with function Intersection(Left, Right : Set) return Set is <>; with function Empty return Set is <>; with function Unit_Set(With_Item : Element) return Set is <>; with function Nth_Element(Of_Set : Set) return Element is <>; package Set_Signature is end; ... we could define a generic that required some set abstraction, but it didn't care which one so long as it implemented the above signature: generic with package Base_Set is new Set_Signature(<>); package Layered_Abstraction is type Cool_Type(Set : access Base_Set.Set) is limited_private; procedure Massage(CT : in out Cool_Type; New_Set : Base_Set.Set); ... end Layered_Abstraction; Now if we want to define a set type and provide the pre-instantiated signature for it, we run into trouble: generic type Elem is private; with function Hash(El : Elem) return Integer; package Hashed_Sets is type Set is private; function Union(Left, Right : Set) return Set; ... package Signature is new Set_Signature(Elem, Set); private type Set is record ... end record; end Hashed_Sets; The problem is that we can't do the instantiation of Set_Signature where we would want to do so, because the instantiation freezes the type "Set" prematurely. A similar problem occurs when a type wants to include a pointer to a container based on the type being defined. For example: package Expressions is type Expr_Ref is private; package Expr_Sequences is new Sequences(Expr_Ref); -- Freezing trouble here! type Seq_Of_Expr is access Expr_Sequences.Sequence; function Operands(Expr : Expr_Ref) return Seq_Of_Expr; ... private type Expression; -- completion deferred to body type Expr_Ref is access Expression; end Expressions; Here we have a case where we want to instantiate a generic using a private type, and use the results of the instantiation as the designated type of an access type, which is in turn used as a parameter or result type of an operation on the private type. Unfortunately, we can't instantiate the "Sequences" generic with Expr_Ref, since it is private. !proposal (See summary.) !wording Change paragraph 12.3(12) as follows: A generic_instantiation declares an instance; it is equivalent to the instance declaration (a package_declaration or subprogram_declaration) followed [immediately] by the instance body[, both at the place of the instantiation]. {The instance declaration is located at the place of the instantiation. The instance body is located immediately following the instance declaration if the generic is not preelaborated. If the generic is preelaborated, the instance body is located immediately prior the first of: o the next reference to a subprogram, protected subprogram, or generic unit declared within the instantiation; o the next freezing point for a task or private type declared within the instantiation; o the next non-instance body; o the end of the nearest enclosing declarative_part or library_item.} Change paragraph 12.3(20) as follows: ... Finally, the instance declaration [and body are] {is} elaborated. {The instance body is elaborated later, at the place where it (implicitly) occurs.} Change paragraph 13.14(5) as follows: o The occurrence of a generic_instantation causes freezing {except when the generic is preelaborated}; also, if a parameter of [the] {an} instantiation {that causes freezing} is defaulted, the default_expression or default_name for that parameter causes freezing. {For an instantiation that does not cause freezing, the constructs of the instance declaration cause freezing according to the rules of this clause; at the place where the instance body (implicitly) occurs, it freezes the parameters of the instantiation, and the default_expressions or default_names for all defaulted parameter cause freezing.} !example (See problem.) !discussion Signature packages can be a very useful feature, but if they can't be instantiated in the generic that defines the abstraction, they become of much less use. Generics in general can be very useful, but the freezing rules mean that they are not as powerful as cut-and-paste, since they can't be applied to private types before the private type is completely defined. This proposal makes it possible for a generic instantiation to be used in more places than before, since it can be used with a private type before the type is completely defined, provided that the generic is preelaborated. As things stand now, it is often necessary to completely restructure things to be able to use a generic container as part of defining a private type. This proposal is based on preelaborability, because the elaboration of a preelaborated unit has no visible effect, other than establishing that the entities it declares can be used (called, instantiated, activated). Therefore, moving the place where elaboration occurs has no effect, provided that it doesn't cause access-before-elaboration problems. To avoid such problems, the location of the instance body is guaranteed to always occur before any construct that could fail an Elaboration_Check. This has the nice property of maintaining the invariant that there is never any Elaboration_Check for entities exported by an instance. Note that the rule about private types is necessary to prevent a privacy violation: a private type exported by an instance might contain tasks, in which case freezing that private type might activate tasks, and this activation might fail an Elaboration_Check. Of course, it is possible for the instance body to be (implicitly) located at a place where some of the actual types are not completely defined, and that's problematic. We solve this by changing the freezing rules so that the instance body freezes the actual parameters. This may lead to compile-time errors with rather mysterious error messages, but hopefully these errors will be infrequent. It's interesting to point out that, when the source code is modified, the location of the instance body is likely to move around, but this is not observable (except that it may cause compile-time errors in rare cases). With this proposal, the following example extracted from the email discussion works as expected: generic type Elem is private; with function Code (I : Elem) return Natural; type Elem_Array is array (Positive range <>) of Elem; package Array_Hashing is function Hash (Arr : Elem_Array) return Natural; end Array_Hashing; package body Array_Hashing is type Nat_Mod is mod Natural'Last; function Hash (Arr : Elem_Array) return Natural is Res : Nat_Mod := 1; begin for I in Arr'range loop Res := Res*3 + Nat_Mod(Code(Arr(I))); end loop; return Natural(Res); end Hash; end Array_Hashing; package User is type Something is private; function Count (A : Something) return Natural; type Array_of_Something is array (Positive range <>) of Something; package My_Hash is new Array_Hashing (Something, Count, Array_of_Something); NULL_HASH : constant Natural; private type Something is range 0 .. 200; NULL_HASH : constant Natural := My_Hash.Hash ((1..0 => 0)); end User; The instance body for My_Hash is located immediately before the call to My_Hash.Hash, so this call doesn't raise Program_Error. The instance body freezes Something, but this type is already completely defined so there is no illegality. --!corrigendum 13.14(05) !ACATS test An ACATS test should be created for this feature. !summary !appendix ****************************************************************