!standard H.4 (08) 98-04-08 AI95-00130/02 !class binding interpretation 96-04-13 !status work item 96-04-17 !status received 96-04-13 !priority Medium !difficulty Medium !subject Enforcing Restrictions may violate the generic contract model. !summary 98-04-08 The enforcement of restrictions will generally violate the contract model of generics, as well as violate the "privateness" of code in a private part or body. To be consistent with the No_Task_Hierarchy and No_Nested_Finalization restrictions, the No_Local_Allocators restriction should not preclude nested generic instantiations. No_Nested_Finalization is broadened to cover finalization associated with protected and task objects. For the purposes of these checks, a generic template is logically expanded at the point of each instantiation, the type definition for a record or protected type is logically expanded at the point of any default-initialized object creation, and default parameter expressions are inserted where used. !question 96-04-13 H.4(8) says: No_Local_Allocators Allocators are prohibited in subprograms, generic subprograms, tasks and entry bodies; instantiations of generic packages are also prohibited in these contexts. Why are instantiations prohibited in these contexts? (This AI allows them.) The restrictions No_Task_Hierarchy and No_Nested_Finalization do not prohibit such instantiations. !recommendation 98-04-08 An implementation supporting the No_Task_Hierarchy, No_Nested_Finalization, or No_Local_Allocators restrictions must enforce the intent of these restrictions with compile-time and/or post-compilation checks. For the purposes of these checks: - Generic instances are logically expanded at the point of instantiation; - If an object of a type is declared or allocated and not explicitly initialized, then all expressions appearing in the definition for the type and any of its ancestors are presumed to be evaluated (even for components that are not present in the created object). - Default formal parameters are presumed to be evaluated only if the corresponding actual parameter is not provided in a given call or instantiation; - Data flow information is not to be used; even "dead" code should be checked for violations. (??? Is this burdensome for implementations that completely remove dead code early? It seems "safe" to ignore dead code so long as it is "really" dead, i.e., no object code is generated for it, but of course the check then becomes overly permissive and hence not as portable. ???) No_Task_Hierarchy means that only tasks directly dependent on the master representing the execution of the environment task (body) are permitted. Tasks dependent on masters which correspond to other bodies or blocks are not permitted, even if these masters are executed by the environment task. No_Nested_Finalization should be broadened to mean that objects requiring finalization due to having a controlled, protected, or task part are not permitted unless they are at the library level. No_Local_Allocators means that allocators are prohibited in subprograms, generic subprograms, task bodies, and entry bodies. As indicated above, rather than precluding nested instantiations, instantiations are to be logically expanded at the point of instantiation for the purposes of this check. !wording 98-04-08 !discussion 98-04-08 Precluding nested generic instances for the No_Local_Allocators restriction in H.4(8) in an attempt to preserve a generic control model for restrictions is inconsistent with the rules for No_Task_Hierarchy given in D.7(3) and for No_Nested_Finalization given in D.7(4). In general, enforcing pragma Restrictions across a partition will necessarily violate the "privateness" of a private part or a body, as well as the generic contract model. Although it might be useful to know that if a generic body does not by itself violate a restriction, then neither will any instantiation, enforcing this kind of "contract" rule for restrictions that distinguish library level from non-library level usages would overly limit the nested instantiations of useful, benign generics. Furthermore, the pragma Restrictions is primarily designed to support application environments where schedulability and formal verification requirements dictate that generics can only be certified with respected to particular instantiations -- never in the abstract. A more serious problem with the rules given for the No_Task_Hierarchy restriction in D.7(3), No_Nested_Finalization in D.7(4), and No_Local_Allocators in H.4(8), is that they do not properly account for violations appearing in expressions used for default parameters and for default initialization. For example, the following partition obeys the static criteria given in H.4(8) for the No_Local_Allocators restriction, yet (in the absence of code-removing optimizations) the main subprogram evaluates an allocator: package P is type Integer_Pointer is access Integer; type R is record C: Integer_Pointer := new Integer; end record; end P; with P; procedure Main is X: P.R; -- X.C is initialized by invoking an allocator begin null; end Main; To close such loopholes, it is necessary to logically substitute default initialization and default parameters in line at the point of usage. However, more or less sophisticated flow analysis might affect which of the expressions appearing in a record or protected type definition are presumed to be evaluated as part of the creation of a default-initialized object of such a type. To establish implementation-independent rules for enforcing restrictions, we choose to follow the lead established by the "freezing" rules where all expressions in a type definition are presumed to be evaluated if any are evaluated (13.14(15)), whereas expressions for default parameters are only relevant where the corresponding actual parameter is omitted (13.14(5,14)). Unlike the freezing rules, an explicitly initialized object is not presumed to evaluate any of the expressions inside the corresponding type definition as part of its creation; only a default initialized object will cause this. !appendix 98-03-16 [Note: The following comment inadvertently refers to H.4(08) as G.4(08).] !section G.4(08) !subject Should No_Local_Allocators disallow nested instantiations? !reference RM95-G.4(08) !from Bob Duff !reference 96-5477.a Robert A Duff 96-4-12>> !discussion The following is a bunch of e-mail I got on the subject of whether No_Local_Allocators should forbid nested instantiations. I think this deserves an AI. I agree that the restriction should be removed. - Bob Date: Fri, 8 Mar 1996 14:53:41 -0500 From: stt@dsd.camb.inmet.com (Tucker Taft) To: hrg@waffle.cise.npl.co.uk Subject: Nested instantiations and H.4(8) A member of the ACVC team brought paragraph H.4(8) to my attention: No_Local_Allocators Allocators are prohibited in subprograms, generic subprograms, tasks and entry bodies; instantiations of generic packages are also prohibited in these contexts. The restriction against nested instantiations seems unnecessary and inconsistent with other restrictions, such as No_Task_Hierarchy and No_Nested_Finalization. The AARM indicates that the restriction against nested instantiations is to preserve the generic contract model. However, there seems no strong argument why the generic contract model need be preserved by pragma Restrictions. Pragma restrictions will often require link-time detection of problems anyway, and so finding out about a problem associated with an instantiation at that point (which is the down side of not preserving the generic contract model) seems OK as well. Hence, I would suggest that the HRG consider removing the part of H.4(8) that follows the ";", because it is inconsistent with other restrictions, and unnecessarily restrictive. -Tuck Date: Fri, 8 Mar 96 16:11:23 EST From: dewar@gnat.com (Robert Dewar) To: hrg@waffle.cise.npl.co.uk, stt@dsd.camb.inmet.com Subject: Re: Nested instantiations and H.4(8) Content-Length: 449 what's the point of preserving generic contract model when the lnker will check for consistency anyway. Moreover, this imitation is a very serious one from a programming point of view, making the pragma Restrictions pretty much useless. If this is not changed, then any reasonable iplementation will have to add pragma Restrictions (No_Local_Allocators_Sensible_Version); so I think we should consider this restriction a mistake and remove it. Date: Mon, 11 Mar 96 09:15:57 GMT From: Brian A Wichmann To: hrg@cise.npl.co.uk, stt@dsd.camb.inmet.com Subject: Re: Nested instantiations and H.4(8) I am not sure how this restriction came about. Does Bob Duff have any idea? Removing it seems fine at first glance... Brian. ------------------------------------------------------------- Brian Wichmann Tele: +44 181 943 6976 (direct) National Physical Laboratory FAX: +44 181 977 7091 Teddington Middlesex e-mail: baw@cise.npl.co.uk TW11 0LW UK WWW: http://www.npl.co.uk/npl/search/staff?Dr+Brian+Wichmann ------------------------------------------------------------- **************************************************************** !section G.4(08) !subject Should No_Local_Allocators disallow nested instantiations? !reference RM95-G.4(08) !reference 96-5478.a Robert A Duff 96-4-12 !from Robert I. Eachus !reference 96-5488.a Robert I. Eachus 96-4-15>> !discussion Bob Duff said: > I agree that the restriction should be removed. So do I, but I don't agree Tuck: > Hence, I would suggest that the HRG consider removing the part of H.4(8) > that follows the ";", because it is inconsistent with other restrictions, > and unnecessarily restrictive. I think instead we need to "elaborate" the original intent: No_Local_Allocators Allocators are prohibited in subprograms, generic subprograms, tasks and entry bodies; instantiations of generic packages _which_contain_allocators_or_task_declarations_ are also prohibited in these contexts. But this also has an AARM implication. An implementation might normally implement generic packages by allocating space on the heap, and in the case of nested packages, recovering the space once the scope is left. Such an implementation should be forbidden in the presence of this pragma. Back to Tuck: > Pragma restrictions will often require link-time detection > of problems anyway, and so finding out about a problem associated > with an instantiation at that point (which is the down side of not > preserving the generic contract model) seems OK as well. Notice that it is only nested generic package instantiations that are forbidden. Nested generic subprogram instantiations are permitted and instantiations with formal private type parameters could require the link time check: generic type Fubar is private; procedure Foob; procedure Foob is X: Fubar; begin ... end Foob; ... procedure Main is type Foo is record Bar: Pointer := new Hidden_Allocator; end record; procedure FB is new Foob(Foo); .... (Gee, running low on metavariables there...) Since, absent the pragma there is no dependence of the body of Main on the body of Foob, the violation of the pramga must be detected after compile time. **************************************************************** >From the minutes of the November 1997 ARG meeting: Erhard argues, in a separate e-mail message, against the current position of the AI because it breaks the contract model and requires too much link time mechanism to enforce the restriction. Instead enforcing the currently stated restriction of only library-level instantiations will enforce the contract model by doing a compile-time check. Pascal and Robert then noted that there is a much deeper problem here. Record components with default initialization, protected objects with initialization of their state, default values for parameters all share the property that allocations might happen that are not apparent at source level. The current semantics of No_Local_Allocation does not cover these cases at all. Since these types can be private, the privateness principle will need to be broken for compile-time checks. The group could not come up with "circumstantial" restrictions that would otherwise ensure the absence of such implicit allocation without being extremely and unacceptably over-restrictive. In passing, it was noted that the strict predicate of No_Local_Allocators is actually value-dependent (and hence undecidable), since a default-initialized component might be located in a non-existent variant of a constrained record object. There is a concern that this sets a precedence for exposing more implementation information about types, which violates the spirit of abstract data type information hiding (and will cause problems in some compilers). But the Restrictions facility is inherently implementation focused (especially restrictions like this one that deal with nested usage, No_Nested_Finalization and No_Task_Hierarchy being the other ones) and therefore some exposure of the implementation details of imported types (such as Text_IO that uses allocated data for file_type) must be made in order to enforce the Restrictions. This will affect semantic analysis involving private types because now uses of private types will need to check that they adhere to restrictions relating to previously hidden features of the private type. The No_Task_Hierachy and No_Nested_Finalization restrictions share the same problem, since components might be tasks or of controlled type. It is not clear what the language can do about the portability of these three pragmas and still be effective. It was even suggested that these three restrictions should not be in the language, after all, since the benefits do not justify the costs. ASIS-based analysis tools were mentioned as an alternative restriction-enforcing mechanism. Norm then suggested to take a step back and reformulate the semantics of the pragma from the viewpoint of intended usage, and leave the formulation of secondary restrictions to the implementation. He will rewrite to emphasize usefulness over portability; he will coordinate with Erhard on the write-up. **************************************************************** --------------------------------------------------------------------------------