!standard 13.14(3/4) 15-03-26 AI05-0155-1/03 !class binding interpretation 15-02-20 !status work item 15-02-20 !status received 15-02-20 !priority Low !difficulty Easy !qualifier Omission !subject Freezing of operations of incomplete types with completions deferred to a body !summary When a profile is frozen at the end of a declarative part or package specification, any incomplete types that the profile contains are not frozen. !question Consider: package Pack is type Bag_Type is private; procedure Add (Bag : in out Bag_Type; Elem : Integer); private type Set is tagged; type Bag_Type is access Set; package Inner is -- These can't be primitive for Set, else 3.10.1(9.3/2) is violated. procedure Add (Elem : Integer; To_Set : in out Set); procedure Union (Left, Right : in Set; Result : out Set); procedure Intersection (Left, Right : Set; Result : out Set); function "=" (Left, Right : Set) return Boolean; end Inner; end Pack; -- Freezing here. 13.14(3/4) [and earlier versions as well] says that everything excepting incomplete types are frozen at the end of Pack. This includes the profiles of any subprograms declared within Pack. 13.14(14/3) tells us that freezing of a profile freezes each subtype of the profile. That means, for example, that freezing the profile of Union freezes Set. But freezing of Set is illegal by 13.14(17/3) [the type is not yet complete]. [This is why there is an exception for incomplete types in 13.14(3/4), otherwise any incomplete type with the completion deferred to a body would be illegal.] Thus all of the declarations in package Inner are illegal. The changes to the usage of incomplete types in Ada 2012 (AI05-0151-1) would suggest that these subprograms should be legal. They could be called from a child unit without that child having to see the full type for Set, so they seem potentially useful. Should such subprograms be allowed? (Yes.) !recommendation (See Summary.) !wording Modify 13.14(3/3) as modified by AI12-0103-1: The end of a declarative_part, protected_body, or a declaration of a library package or generic library package, causes freezing of each entity and profile declared within it, except for incomplete types. A proper_body, body_stub, or entry_body causes freezing of each entity and profile declared before it within the same declarative_part that is not an incomplete type; it only causes freezing of an incomplete type if the body is within the immediate scope of the incomplete type. {Freezing of a profile in these cases does not freeze an incomplete subtype of the profile.} !discussion 13.14(3/4) has an explicit “hole” so that incomplete types whose completion is given in the body (so-called Taft-Amendment types) do not freeze. Otherwise, such incomplete types would be illegal as 13.14(17/3) would be violated when they were frozen at the end of the scope. Ada 2012 added explicit rules allowing operations like the ones in the question. It seems bizarre that these operations are always illegal because of freezing; it reduces the value of the new rules. Operations like the ones in the question can be useful to provide operations to child units of a subsystem on a Taft-Amendment type without having to make the full declaration visible. Such operations are limited to non-primitive operations as any primitive operations of a Taft-Amendment type are illegal by 3.10.1(9.3/2). However, as noted in the question, it's easy to declare non-primitive operations by adding a nested package; this trick is widely known among expert Ada programmers, so it's likely to be used in this case. ---- The reason that we need to freeze incomplete types is discussed in AI05-0017-1. In particular, it's necessary to prevent premature dispatching calls for ordinary (not Taft-Amendment) incomplete types. (Taft-Amendment incomplete types can't have primitive operations, so no dispatching calls can be made on an operations of them.) Therefore, we only exclude freezing of incomplete types in contexts where no further premature calls can be made (the "global freezing contexts", or dispatching calls are impossible for other reasons. ---- Notes on the 13.14(3/4) wording: We only need to say that incomplete types of the profile are not frozen. If the incomplete type is not one that is exempt from freezing, it will necessarily be frozen as well by one of these "global freezing contexts", and thus we don't need to specifically include those in profile freezing. The wording "subtype of a profile" seems a bit non-specific, but it echos the wording of 13.14(14/3) [which is the normal case for freezing a profile]. There is no reason for this to be different. ---- The author created a complete ACATS-style test for an example like the one in the question. The test uses the operations in a child package where the completion is unknown. (This is usefully possible because the type is tagged incomplete, thus calls can be made so long as the object is passed intact.) Compiling this program on GNAT shows that GNAT does not detect the freezing violations. However, the child package containing the calls generates a lovely internal error message. Because of this, it is not possible to run the program. As such, it seems unlikely that anyone is depending on these operations working; neither the RM nor the only known fielded Ada 2012 implementation allows them. That's not particularly surprising; the capability was added by Ada 2012 and Taft-Amendment types aren't that common, so most likely no one has yet run into the intersection of both. ---- ** Temporary (?) note ** Interestingly, this fix would not allow the original ACATS test that brought up this question. In that test, a generic with a formal tagged incomplete type is instantiated, passing the Taft-Amendment type and the operations declared in the nested package. This new rule allows the operations to be declared without causing freezing problems. However, an instance freezes the profiles of formal subprograms except for those with *untagged* formal incomplete parameters. (Subprograms with tagged incomplete parameters freeze as they can be called within the subprogram, and calling an unfrozen profile is bad news.) We did not add any exception for the freezing of such profiles, thus 13.14(17/3) would be violated and the instance is illegal. We could have added another exception to 13.14(10.2/3) to allow that program. Something like: "Freezing a profile in this case only freezes types that are not tagged incomplete." Argubly, that should also be done, as it's somewhat odd to allow calls to subprograms but not to allow the same subprogram to be used as a formal parameter. We didn't add such a rule as almost all *useful* calls to subprograms with incomplete parameters have to occur in some other compilation unit (typically, a child unit). Moreover, all such local calls already are freezing violations. [Calls of subprograms with parameters of tagged incomplete types are explicitly allowed by 3.10.1(10/3) (note that it only ban actual parameters of *untagged* incomplete views). Since all calls freeze the profiles of the associated subprograms, any (local) call whose parameters are tagged incomplete types without a completion will be illegal because of 3.10.1(9.3/2). That's intentional for cases like the one in AI12-0017-1.] Calls in other units are OK, *assuming* that any entity imported via a *with* (or from a parent, which is another form of semantic dependence; those should all be treated equivalently) is already frozen (somewhere). That makes sense, as some other unit must have the completion or the partition is illegal. However, the language rules don't make this clear. It's definitely significant for various kinds of incomplete types -- we surely don't want anyone to think that incomplete views from a limited with need to have a completion visible somewhere in order to declare or call a subprogram, and the same is true for Taft-Amendment incomplete types. With that assumption, we don't need to fix the generic problem, although it means that a generic cannot be instantiated in the same scope as these operations. (Which is annoying, but not fatal, as the instance could have been in the child unit, where no freezing would apply [we assume!].) Note that this instance caused an internal error in GNAT very similar to the one caused by the calls in the ACATS-style test for the example in the question. ** End Temporary (?) note ** !corrigendum 13.14(3/4) @drepl @dby !ASIS No ASIS effect. !ACATS test The problem was originally discovered in ACATS test CC51011, so some version of that test will test the fix. There also is the proposed test named C3A1005, a version of CC51011 without the generic units. If this AI is approved, that test can be issued (it tests some moderate priority 3.10.1 objectives that are currently on the testing list -- this AI would raise that priority somewhat). !appendix From: Randy Brukardt Sent: Friday, February 28, 2015 5:52 PM I have had a running conversation with an implementer over a series of tests, which has led to a question that suggests that there might be a problem (really an oversight) in the freezing rules. (Imagine that!) Here's the important part of the example: package Pack is type Bag_Type is private; procedure Add (Bag : in out Bag_Type; Elem : Integer); private type Set is tagged; type Bag_Type is access Set; package Inner is -- These can't be primitive for Set, else 3.10.1(9.3/2) is violated. procedure Add (Elem : Integer; To_Set : in out Set); procedure Union (Left, Right : in Set; Result : out Set); procedure Intersection (Left, Right : Set; Result : out Set); function "=" (Left, Right : Set) return Boolean; end Inner; end Pack; -- Freezing here. The implementer's complaints led me to look at how this freezes. 13.14(3/4) [and earlier versions as well] says that everything exception incomplete types are frozen at the end of Pack. This includes the profiles of any subprograms declared within Pack. 13.14(14/3) tells us that freezing of a profile freezes each subtype of the profile. That means, for example, that freezing the profile of Union freezes Set. But freezing of Set is illegal by 13.14(17/3) [the type is not yet complete]. [This is why there is an exception for incomplete types in 13.14(3/4), otherwise any Taft-Amendment type would be illegal.] Thus all of the declarations in package Inner are illegal. This only matters to Taft-Amendment types like Set in the example. For a type that is completed in the package specification, 13.14(17/3) does not apply at the end of the package, because the type is in fact complete. For an incomplete view, one assumes that types that are imported were previously frozen by the end of the compilation unit that contains them and thus don't need any freezing. [BTW, I can't find any justification for this view in the RM wording; it's something that I wish we'd fix because it comes up periodically, but it also seems to fit the Dewar rule as no other interpretation makes sense (at least to me). And since it fits the Duff rule as well (no one would ever change behavior because of the fix), I've never made an AI for it. Interestingly, I made this very same point in the e-mail in AI12-0151-1 (June 6, 2009), so maybe it's mainly this case where it matters. That e-mail specifically argued that there was no freezing problem for incomplete views from limited withs for this sort of case, in response to a question from Tucker. It must have satisified everyone, because no further discussion occurred on that point.] Note that there is no problem with these profiles freezing without knowing the full type. We allow calls on similarly defined subprograms when the parameter type is a tagged incomplete view defined by a limited with. The parameters have to be passed by reference, and dispatching calls are prevented by 3.10.1(9.3/2) [primitives aren't allowed]. Such routines can be used in child packages without having to make the actual implementation visible. That seems useful to me. (Maybe not useful enough to bother changing the language, but certainly useful enough to disqualify this case from the ARG gallery of pathologies, honoring Steve Baird. :-) The freezing requirement here says that the expansions in usage of incomplete types defined in Ada 2012 are completely unusable for Taft-Amendment types. Any use of a Taft-Amendment incomplete type directly as a parameter or result will necessarily result in a freezing violation. It's hard to believe we intended that. OTOH, AI12-0151-1 talks exclusively about incomplete views coming from limited views. As such, it's possible that we didn't care at all about any changes that happened for any other kind of access type. Either way, I'd like to have a decision. If the freezing rule stands, then a couple existing tests and a number of objectives are unnecessary, and I need to clean up the ACATS and its documentation to reflect that. And if it is going to be relaxed somehow, I'm sure the implementer would be interested, and the test objectives probably will need a couple of additional cases. For what it's worth, GNAT appears to accept the example package (and its body) without complaint. I didn't try to create a runnable example, so I don't know if the result would actually work or if I would get to see one of GNAT's famous bug boxes. I'll do that IFF there is some interest in exploring whether the freezing rules should allow examples like this. (Of course, if we confirm the language, meaning the above is illegal, I'll make a B-Test out of the example to ensure that it is getting rejected.) ****************************************************************