Version 1.3 of ai12s/ai12-0103-1.txt
!standard 13.14(3/3) 14-11-13 AI12-0103-1/03
!standard 13.14(5/3)
!class binding interpretation 14-05-12
!status Corrigendum 2015 14-11-13
!status ARG Approved 7-0-1 14-10-18
!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; --
private
type Priv is record
Len : Natural;
Data: Natural;
end record;
--
function Is_Empty (A : Priv) return Boolean is
(A = Empty_Priv or else A.Len = 0); --
--
Empty_Priv : constant Priv := (Len => 0, Data => 0); --
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 {proper_body, body_stub, 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, the syntax terms proper_body, body_stub, and
entry_body 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 "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.
[Editor's note: 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 proper 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, so they don't need to covered by the above rule.
Add after 13.14(5/3):
* At the occurrence of an expression_function_declaration that is a completion,
the expression of the expression function causes freezing.
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; --
function Foo return Integer is (Bar); --
function Foo return Integer is --
begin
return Bar;
end Foo;
Clearly, there is going to be a wart of some sort 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; --
function Foo (A : in Natural) return Natural is
(A + Flub'Size); --
Val : Natural := P.all(5); --
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.
!corrigendum 13.14(3/3)
Replace the paragraph:
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 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.
by:
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.
!corrigendum 13.14(5/3)
Insert after the paragraph:
- The occurrence of a generic_instantiation causes freezing, except that
a name which is a generic actual parameter whose corresponding
generic formal parameter is a formal incomplete type (see 12.5.1)
does not cause freezing. In addition, if a
parameter of the instantiation is defaulted, the default_expression or
default_name for that parameter causes freezing.
the new paragraph:
- At the occurrence of an expression_function_declaration that is
completion, the expression of the expression function causes freezing.
!ASIS
No ASIS effect.
!ACATS test
An ACATS B-Test should be created to check these freezing rules, possibly with
a matching C-Test to ensure that allowed cases actually execute properly.
!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