Version 1.2 of ai12s/ai12-0103-1.txt

Unformatted version of ai12s/ai12-0103-1.txt version 1.2
Other versions for file ai12s/ai12-0103-1.txt

!standard 13.14(3/3)          14-07-30 AI12-0103-1/02
!class binding interpretation 14-05-12
!status work item 14-05-12
!status received 14-04-10
!priority Low
!difficulty Medium
!subject Expression functions that are completions in package specifications
!summary
Expression functions that are a completion freeze their expression but do not freeze anything else (unlike regular bodies). Other expression functions don't freeze anything at all.
Similarly, null procedures never cause freezing, even if they are completions.
!question
Consider the following:
package P is type Priv is private;
Empty_Priv : constant Priv;
function Is_Empty (A : Priv) return Boolean; -- (1)
private
type Priv is record Len : Natural; Data: Natural; end record;
-- Completions still need checking (call case): function Is_Empty (A : Priv) return Boolean is (A = Empty_Priv or else A.Len = 0); -- (2) -- Not used in this package, so not frozen until the end of the package.
Empty_Priv : constant Priv := (Len => 0, Data => 0); -- (3) Freezes Priv.
end P;
Is_Empty (2) is an expression function representing a completion, but 13.14(3/3) only applies in declarative_parts, while this is a package specification. So the expression function Is_Empty is does not cause freezing at (2).
The completion of Empty_Priv at (3) is OK, as there is no requirement that Is_Empty uses only complete types.
Erasing the declaration of Is_Empty at (1) has no effect.
This seems to be what we want. If 13.14(3/3) did apply to package specifications, then the completion (2) would be illegal (as it is using an incomplete deferred constant), but it could be made legal by erasing the declaration at (1).
Is this correct? (Not quite.)
!recommendation
(See summary.)
!wording
Modify AARM 13.14(3/3):
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 {body or entry_body}[noninstance body other than a renames-as-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.
[Editor's note: In the new text, "body" and "entry_body" are in the syntax font. These do not include instances or renames, so we no longer need the exceptions in the old text.]
Replace AARM 13.14(3.f/3) with:
Note that "body" includes "proper_body"s and "body_stub"s; these, along with "entry_body"s cause freezing. However, "null_procedure_declaration"s and "expression_function_declaration"s (even when those are used as completions), as well as "generic_instantiation"s and renames-as-bodies do not necessarily cause freezing; each has their own specific rules.
[All of the quoted things are in the syntax font, without quotes, in the actual note.]
Add an AARM Ramification after 13.14(3.g/3):
Note that the rule about bodies being freezing only applies in declarative_parts. All of the kinds of bodies (see 3.11.1 - keep in mind the difference from "body"s) that are allowed in a package specification have their own freezing rules. Freezing is annoying to users, so we want as little of it as makes semantic sense.
Add after 13.14(5/3):
* The occurrence of a expression_function_declaration that is a completion
causes freezing of the expression of the expression_function_declaration.
AARM Reason: This rule prevents calls through access values to an expression that might have unfrozen parts. Typically, elaboration checks and other freezing rules prevent this, but in this case the completion is elaborated but since this is not a "body" [syntax font] it does not by itself freeze anything that precedes it.
Modify AARM 13.14(10.g/3) so that "body" is in the syntax font (two places).
!discussion
The general principle is that things that might occur in a package specification freeze as little as is required to prevent semantic problems. In particular, renames-as-bodies are excluded from the "freeze everything" rules (despite acting as a body).
This is necessary so that the occurrence of one of these implicit bodies don't freeze unrelated entities from outer scopes.
Moreover, it's clear that 13.14(3/3) does not apply to package specifications. The reason for the rule, as explained by the following AARM notes, is that we want interchangability of bodies (of all kinds) with body stubs (which have to be freezing, because we can't know their contents). However, body stubs are not allowed in package specifications. So there is no need to have rules that emulate having those.
Additionally, there is a usability issue. Freezing is mysterious to users (one user said they believed that it was invented to cover up compiler bugs!) As such, we should require as little freezing as possible to avoid semantic problems.
The primary argument for expression functions as completions freezing is for consistency with other bodies. But that is misleading, as renames-as-bodies are not freezing. So changing from a renames-as-body to an expression function to an regular body has to change freezing somewhere. Specifically, all of the following have essentially the same effect (assume Bar is a function, and these Foos are all completions):
function Foo return Integer renames Bar; -- Never freezing
function Foo return Integer is (Bar); -- Freezes only Bar (see below)
function Foo return Integer is -- Always freezes begin return Bar; end Foo;
Clearly, there is going to be a wart of sorts in any case. Looking at all kinds of bodies (semantic font), we have a continuum of kinds of bodies:
null procedures as completions; renames-as-body; expression functions as completions; generic instantiations; normal bodies ("proper_body" and "entry_body"); body stubs.
It's clearly never going to be the case that all of these have the same freezing rules. Thus we simply have the existing rule (13.14(3/3) only apply to the last two, and change the AARM notes accordingly.
Unfortunately, this is not quite the end of the story. Steve Baird pointed out that a modified version of the example in AARM 13.14(10.i-q/3) causes trouble:
package Pack is type Flub is range 0 .. 100; function Foo (A : in Natural) return Natural; type Bar is access function Foo (A : in Natural) return Natural; P : Bar := Foo'access; -- (A) function Foo (A : in Natural) return Natural is (A + Flub'Size); -- (B) Val : Natural := P.all(5); -- (C) end Pack;
13.14(10.3/3) does not apply to (A) (since the declaration of Foo is not an expression function). There is no rule (intentionally) to freeze the expressions of an expression function, so (B) does not freeze anything. However, then, the call at (C) is using a property of an unfrozen type.
Moreover, note that the call is after the body of Foo, so it passes elaboration checks.
We briefly considered changing elaboration checks to prevent this problem, but it would change something simple (elaboration checks) to fix something that is already complex (freezing).
Thus, we have adopted a rule that an expression function as completion freezes its expression (so it can be immediately called, as it is elaborated).
We could have complicated the rule so that it only applies when 'Access is taken of the subprogram within the immediate scope, but that would be an unusual context dependency for a freezing rule.
!ASIS
No ASIS effect.
!ACATS test
An ACATS C-Test should be created to check this; it would be similar to
!appendix

From: Randy Brukardt
Sent: Wednesday, April 23, 2014  8:21 PM

I recently enhanced a new ACATS test with the following test case (there's an
illegal case as well, not shown here because its not relevant to this
discussion):

...

   type Priv is private;

   Empty_Priv : constant Priv;

   function Is_Empty (A : Priv) return Boolean;

private

   type Priv is record
      Len : Natural;
      Data: Natural;
   end record;

   -- Completions still need checking (call case):
   function Is_Empty (A : Priv) return Boolean is
      (A = Empty_Priv or else A.Len = 0);     -- OK.
      -- Not used in this package, so not frozen until the end of the package.

   Empty_Priv : constant Priv := (Len => 0, Data => 0); -- OK. Freezes Priv.

...

[Note: The implementers are anonymous here because the ACAA Technical Agent must
never identify implementers. In this case, I'm pretty sure everyone would not
mind being identified, but I'll stick to the rules.]

An implementer E complained that Is_Empty is a body by 3.11.1(1/3), and bodies
always freeze everything. In such a case, the reference to Empty_Priv in
Is_Empty is illegal, as Empty_Priv is not yet complete.

Another implementer G said that E was wrong, as the second sentence of
13.14(3/3) specifies that behavior in a declarative_part, but this is a package
specification, which is not a declarative_part. Or that possibly the second
sentence of 13.14(3/3) was wrong because it didn't include package
specifications.

I agree with G, in that the language as literally written says the above is
legal. However, it's fairly clear from AI05-0177-1 that the freezing of an
expression_function used as a body was intended to apply everywhere. AI05-0177-1
also contains an Editor's Note (really an Editor's Gripe) that freezing of
expression functions in package specifications was "killing a fly with a
bazooka"; there's no technical need (one of the problems noted can't happen in a
package specification and the other is already handled by the last sentence of
13.14(10.1/3) since it turned out it applied to all expression functions so we
couldn't ignore it anyway). Moreover, my original intent is that expression
functions behaved like renames-as-bodies, and those do NOT freeze.

I apparently got what I wanted, but not because we intended it. :-)

It should be noted that an expression function that is not a completion is NOT
freezing; this rule only applies to expression functions that are completions.
So it's going to be a bit weird no matter what the rule is - it's not possible
that all of renames-as-bodies, expression functions that are not completions,
expression functions that are completions, and function bodies are consistent.

In any case, since there is a problem with the wording, I'll remove this test
case from the test, so there is no rush to resolve this rather unimportant
quirk.

There seem to be three possible fixes to the Standard:

(1) Do nothing, leave the wording as it is. There is no technical problem (there
    is no way to use an unfrozen entity whether or not expression_functions used
    as completions are freezing). It's a bit inconsistent, because the behavior
    would be different in specifications and in bodies, but that's justifiable
    and harmless (no one is moving the entire private part of a package to the
    body, and moving just the expression function would not show any behavior
    change). This is more flexible as expression functions would not necessarily
    have to follow the completions of deferred constants or any representation
    clauses. OTOH, E complained that their implementation does treat these as
    freezing and would require some amount of work to change that (they are
    generating the body at the location of the completion, that would have to be
    delayed).

(2) Change the wording of 13.14(3/3) so that package specifications are included
    in the second sentence. This might require reordering of
    expression_functions used as completions in the private part in some cases.
    I can't think of any case where that would be impossible, but that might
    just be a lack of imagination (given there are bounds on where these can be
    declared because all declarations that they use have to be visible at the
    point of the expression function). It's possible, however, for that
    completion to be in the visible part. It would not be possible to reorder
    that make it legal as the deferred constant completions are necessarily in
    the private part -- assuming one wanted the expression function to remain
    visible. However, in that case the declaration is redundant and it could be
    removed to fix the problem (as noted eariler, an expression function that is
    NOT a completion is NOT freezing). E would be happy, as this would make his
    implementation correct. I'm still concerned that doing this would make it
    much more likely that users would stumble over these rules, but perhaps that
    isn't very important as I can't find a case where some reordering/removal
    would not give the intended effect.

(3) Change the rules so completions of expression functions (and null procedures
    as well, since we've intentionally made these the same) are never freezing;
    they would act like renames-as-body in that case. A tiny change to
    13.14(10.1/3) would be needed ("function" => "subprogram" in the first
    sentence), as procedure calls would also need profile freezing (although the
    body could only be a null procedure in that case). The downside of this
    change is that switching from the shorthand expression function form to the
    full function body form (or vice-versa) would change the freezing behavior,
    and that might even cause the package body to become illegal in extreme
    cases.

As you can tell, the differences between these are slight, so this isn't very
important. [!priority Low] (2) has the advantage of matching the existing
implementation (and the !discussion of AI05-0177-1). (1) has the advantage of
matching the intent of the designer (me) and would mean that all forms of
completion allowed in package specifications -- renames-as-body, null
procedures, and expression functions would work the same. And for me, freezing
is one of those things that we want as little of as possible; there is
definitely less freezing in (1) than in (2).

I'd like a bit a guidance before writing this up. Thoughts?

****************************************************************

From: Jeff Cousins
Sent: Tuesday, April 29, 2014  8:35 AM

I have some preference for (1) over (2), users don't in general like freezing
and some think that "freezing rules" are just something that compilers (and
their vendors) shout when they don't want to admit a bug. But I could live with
(2).  I don't like (3).

****************************************************************


Questions? Ask the ACAA Technical Agent