Version 1.1 of ai05s/ai05-0296-1.txt
!standard 13.14(10) 12-02-15 AI05-0296-1/01
!class Amendment 12-02-15
!status Amendment 2012 12-02-15
!status work item 12-02-15
!status received 12-01-12
!priority Low
!difficulty Easy
!subject Freezing of actual subprograms which have parameters of formal incomplete types
!summary
The profile of actual subprograms of generic instantiations are not frozen
if the profile of the formal subprogram contains a formal incomplete type
of the same generic.
!proposal
All of the containers packages contain code like the following:
type Cursor is private;
...
function Has_Element (Position : Cursor) return Boolean;
package List_Iterator_Interfaces is new
Ada.Iterator_Interfaces (Cursor, Has_Element);
This is illegal, because 13.14(10.2/3) says that an instantiation freezes
the profiles of all callable entities that it freezes.
!wording
Modify 13.14(10.2/3):
At the place where a generic_instantiation causes freezing of a
callable entity, the profile of that entity is frozen{
unless the formal subprogram corresponding to the callable entity has a
parameter or result of a formal incomplete type of the same generic};
if the callable entity is an expression function,
the expression of the expression function causes freezing.
!discussion
Note that this wording does NOT change the freezing of the actual subprogram
itself; that is still frozen by the instantiation. Only the freezing of the
profile is changed.
It is OK to pass an unfrozen subprogram to a generic in this case, because
almost no calls on that subprogram are legal inside the generic (by
3.10.1(10/3)), and the few that are require all of the incomplete parameters to
be by-reference (and any other parameters will have to be frozen in order for
objects of the appropriate type to exist).
[Question: We have some hairy rules to allow generic children in some similar
cases. (I can't find an example, sorry. It's something about "in the scope of
the generic specification", I think.) My wording will not work in such a case;
do we need it to?
i.e.
generic
type Inc;
package GParent ....
generic
with function Foo (A : Inc) return Boolean;
package GParent.Child ....
package P is new GParent (Something);
package C is new GParent.Child (Bar); --
--
]
!corrigendum 13.14(10)
Insert after the paragraph:
- At the place where an expression causes freezing, the type of the
expression is frozen, unless the expression is an enumeration literal used as a
discrete_choice of the array_aggregate of an
enumeration_representation_clause.
the new paragraph:
- At the place where a generic_instantiation causes freezing of a
callable entity, the profile of that entity is frozen
unless the formal subprogram corresponding to the callable entity has a
parameter or result of a formal incomplete type of the same generic; if the
callable entity is an expression function, the expression of the expression
function causes freezing.
!ACATS Test
An ACATS test that checks that freezing is not premature on such formal
subprograms is needed.
!ASIS
No change needed.
!appendix
From: Edmond Schonberg
Sent: Thursday, January 12, 2012 10:00 AM
The freeze rule for formal incomplete types states:
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 current phrasing suggests that all other actuals in an instantiation are
frozen. However, the following fragment, that appears in all container
packages, suggests that the rule has to apply transitively to entities whose
freezing might freeze such a type:
type Cursor is private;
...
function Has_Element (Position : Cursor) return Boolean;
package List_Iterator_Interfaces is new
Ada.Iterator_Interfaces (Cursor, Has_Element);
Cursor is the actual for a formal incomplete type, and is not frozen. However
Has_Element cannot be frozen either, because freezing a subprogram freezes its
profile, and one of the types in its profile cannot be frozen. Possible
phrasing:
The occurrence of a generic instantiation causes freezing, except in the
following cases:
o 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.
o A subprogram whose profile mentions such a type does not cause freezing.
****************************************************************
From: Randy Brukardt
Sent: Thursday, January 12, 2012 2:00 PM
> Cursor is the actual for a formal incomplete type, and is not frozen.
> However Has_Element cannot be frozen either, because freezing a subprogram
> freezes its profile, and one of the types in its profile cannot be frozen.
Umm, no, freezing a subprogram does *not* freeze its profile; that was the
entire point of separating profile freezing out. Only a *call* freezes a
profile.
The problem here is actually 13.14(10.2/3), which says that a *generic instance*
freezes the profile of any callable entities that it freezes. We presumably need
an exception to this rule for formal subprograms with formal incomplete type
parameters.
But I have to wonder if that really works. If the body of the generic contains a
call on the formal subprogram that isn't frozen, we'd potentially be making a
call on a subprogram with an unfrozen profile. That does not sound good. I'm not
sure off-hand whether such calls are legal anyway (perhaps they're only legal
for tagged incomplete types, which are not a problem since the parameter passing
mode is known anyway). So maybe there is no problem with unfrozen calls.
Anyway, more thought is needed; perhaps Steve can apply his amazing ability to
find problems in feature combinations to this concern.
****************************************************************
From: Gary Dismukes
Sent: Thursday, January 12, 2012 2:05 PM
...
> The current phrasing suggests that all other actuals in an
> instantiation are frozen. However, the following fragment, that
> appears in all container packages, suggests that the rule has to apply
> transitively to entities whose freezing might freeze such a type:
It looks like you've identified a real problem.
...
> The occurrence of a generic instantiation causes freezing, except in
> the following cases:
> o 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.
>
> o A subprogram whose profile mentions such a type does not cause
> freezing.
I think the wording for this case might need to be something more like the
following:
The name of a subprogram corresponding to a formal subprogram whose
profile mentions (or includes the name of?) a formal incomplete type
does not cause freezing.
One question is whether other types mentioned in the profile might still need to
be frozen. It seems that the formal subprogram can't really be used within the
generic itself, due to the various restrictions on incomplete types, so it
should be safe to exempt the subprogram itself from causing freezing, as you
suggest. (Based on a phone conversation that Steve and I had about this.)
****************************************************************
From: Edmond Schonberg
Sent: Thursday, January 12, 2012 2:39 PM
.....
> Umm, no, freezing a subprogram does *not* freeze its profile; that was
> the entire point of separating profile freezing out. Only a *call*
> freezes a profile.
>
> The problem here is actually 13.14(10.2/3), which says that a *generic
> instance* freezes the profile of any callable entities that it
> freezes. We presumably need an exception to this rule for formal
> subprograms with formal incomplete type parameters.
Right, this is the relevant paragraph, not the general freezing of callable
entities. In any case, that's the source of the problem.
> But I have to wonder if that really works. If the body of the generic
> contains a call on the formal subprogram that isn't frozen, we'd
> potentially be making a call on a subprogram with an unfrozen profile.
> That does not sound good. I'm not sure off-hand whether such calls are
> legal anyway (perhaps they're only legal for tagged incomplete types,
> which are not a problem since the parameter passing mode is known
> anyway). So maybe there is no problem with unfrozen calls.
Given that the bodies of containers are full of such calls, this cannot possibly
be a real problem. In the body the full view of that type is available, and
calls can be compiled properly. I wonder whether there will be problems with
shared generics, but that's the kind of concern you can answer better than
anyone! As for the instantiation of iterator.interfaces, it contains only
abstract operations (which is why it can have a formal incomplete type) and thus
there is no problem.
> Anyway, more thought is needed; perhaps Steve can apply his amazing
> ability to find problems in feature combinations to this concern.
Let me introduce the term "Baird territory" to describe those gems, that have
provided such merriment in cold winter nights.
****************************************************************
From: Steve Baird
Sent: Thursday, January 12, 2012 5:49 PM
> The problem here is actually 13.14(10.2/3), which says that a *generic
> instance* freezes the profile of any callable entities that it
> freezes. We presumably need an exception to this rule for formal
> subprograms with formal incomplete type parameters.
I agree with you and Gary; I think this is the right approach.
When you say "for formal subprograms", I assume you mean for actual subprograms
corresponding to such formal subprograms.
And of course, as you said, we are talking only about relaxing the
profile-freezing rules. We'll still freeze the actual subprogram. For example,
if an instantiation actual parameter is
Formal_Subprogram => My_Access_To_Subprogram_Ptr.all
, then we want to freeze the access to
subprogram type regardless of its designated profile.
> But I have to wonder if that really works. If the body of the generic
> contains a call on the formal subprogram that isn't frozen, we'd
> potentially be making a call on a subprogram with an unfrozen profile.
> That does not sound good. I'm not sure off-hand whether such calls are
> legal anyway (perhaps they're only legal for tagged incomplete types,
> which are not a problem since the parameter passing mode is known
> anyway). So maybe there is no problem with unfrozen calls.
Can a generic (spec or body) ever contain a call to a subprogram that has a
parameter or result of a formal incomplete type?
I thought the existing rules combined to disallow any expression/name of an
incomplete type. If they don't, then we have other problems.
If the rules work as I think they do, then any actual parameter the generic
might pass in would be caught. Similarly, any call to a function with an
incomplete result would itself be caught.
So either we don't have to worry about such calls or I am confused.
****************************************************************
From: Randy Brukardt
Sent: Thursday, January 12, 2012 8:41 PM
> I agree with you and Gary; I think this is the right approach.
>
> When you say "for formal subprograms", I assume you mean for actual
> subprograms corresponding to such formal subprograms.
Yes, of course.
> And of course, as you said, we are talking only about relaxing the
> profile-freezing rules. We'll still freeze the actual subprogram.
> For example, if an instantiation actual parameter is
> Formal_Subprogram => My_Access_To_Subprogram_Ptr.all , then we
> want to freeze the access to subprogram type regardless of its
> designated profile.
Yes.
> > But I have to wonder if that really works. If the body of the
> > generic contains a call on the formal subprogram that isn't frozen,
> > we'd potentially be making a call on a subprogram with an unfrozen profile.
> > That does not sound good. I'm not sure off-hand whether such calls
> > are legal anyway (perhaps they're only legal for tagged incomplete
> > types, which are not a problem since the parameter passing mode is
> > known anyway). So maybe there is no problem with unfrozen calls.
>
> Can a generic (spec or body) ever contain a call to a subprogram that
> has a parameter or result of a formal incomplete type?
That was my question. I was rather hoping that you knew for sure, I didn't want
to go spelunking in 3.10.1 (it's uncomfortably close to the Heart of Darkness).
> I thought the existing rules combined to disallow any expression/name
> of an incomplete type. If they don't, then we have other problems.
Surely not *all* such names; I know that the (old) rules for tagged incomplete
types were designed such that calls with parameters of such types would always
work. And I know I constructed such examples (not in a generic, but I doubt
there would be any problem with a generic version).
Those rules surely still exist (compatibility requires it), and thus a formal
tagged incomplete type almost certainly allows calls in some circumstances.
But those aren't a problem.
What worries me here is that we may not have any rules making calls with
incomplete parameters illegal per-se, knowing that the freezing rules would
catch any problems. But here we're eliminating the freezing rules from the
equation (because they can't know if there are any problematic calls) -- which
is a very different territory. As Ed suggested, that seems like "Baird
territory" to me.
We may need a rule that says it is illegal to freeze a non-tagged formal
incomplete type other than at the end of the generic unit containing the
declaration (since there cannot be assumed to be a completion) -- that would
make any calls illegal within the body. Or does that already exist? [Warning
from Randy II: it does in fact exist. Most of you (other than Steve) may want to
stop reading here, because I eventually convinced myself that there isn't a
problem here, and there's three pages of text and examples below. These are left
here for the record, and so that Steve can think of areas to look for the real
problem that surely lurks here...]
> If the rules work as I think they do, then any actual parameter the
> generic might pass in would be caught.
> Similarly, any call to a function with an incomplete result would
> itself be caught.
>
> So either we don't have to worry about such calls or I am confused.
I don't think you're confused, but I do think that such calls are made illegal
for non-generic cases by a combination of freezing rules and other legality
rules. If we modify the freezing rules, we have to make sure the properties
still hold!
It should be noted that I don't think the freezing rules for formal parameters
(inside the generic as opposed to the instance) are well-defined. In particular,
every formal parameter gets frozen somewhere, but we don't really want this to
have any effect and typically ignore that fact. (I think Janus/Ada treats these
as pre-frozen.) But I don't think there is any wording that actually makes this
the model. And that might not be the right model for formal incomplete types in
any case.
To give a quicky (and probably wrong :-) example of what I'm talking about:
procedure Fooey is
type Incomp;
function Is_Flab (A : Incomp) return Boolean;
procedure Flub (A : Incomp) is -- (B)
begin
if Is_Flab (A) then -- (A)
...
end if;
end Flub;
type Incomp is ...;
Obj : Incomp;
begin
Flub (Obj);
end Fooey;
The call at (A) freezing the profile of Is_Flab, and that freezes Incomp, which
is not completed, so this is illegal. (Also, the start of the body at (B) does
the same thing, I think, so this is doubly illegal.)
However, typically freezing of formal parameters does nothing. Surely, it can't
always be illegal to freeze a formal incomplete parameter inside the generic,
since the end of the body (at least) will do that and the alternative is that
all generic units with a formal incomplete parameter are illegal -- a silly
conclusion.
So if we recast the above as a generic:
generic
type Incomp;
package Fooey is
function Is_Flab (A : Incomp) return Boolean;
procedure Flub (A : Incomp);
end Fooey;
package body Fooey is
function Is_Flab (A : Incomp) return Boolean is ...
procedure Flub (A : Incomp) is -- (B)
begin
if Is_Flab (A) then -- (A)
...
end if;
end Flub;
end Fooey; -- (C)
Presuming you agree that freezing Incomp at (C) is cannot be an error, then
there is no reason for it to be true at (A) or (B) either -- in which case I
don't see any rules preventing the call at (A).
So I suspect we need (in addition to the extra exception to freezing of
instances mentioned previously) a rule preventing calls of subprogram whose
profiles contain UNtagged incomplete types or whose result includes any
incomplete type. (As previously mentioned, there is no requirement for freezing
of tagged incomplete types used as parameters; the parameter passing is always
by-reference for such parameters, so there is no need.)
---
Arrggh! After writing all of the above, I found 3.10.1(10/3), which seems to
directly say all of the above already. So there in fact is no problem with the
cases above. But there is a different hole (which is what I was researching when
I found the above rule).
There doesn't seem to be any rule preventing a dispatching call using a tagged
incomplete object (for which the location of the tag is unknown, of course). The
3.10.1(10/3) seems to have been crafted for access-to-subprogram cases (which
cannot be dispatching), and there are special rules to prevent primitive
operations of incomplete types. Humm -- maybe this isn't a problem, since the
operations of a formal incomplete type are never primitive, so they can't be
dispatching.
---
OK, so there isn't really any problem. So you can all ignore the above.
Never mind. :-)
****************************************************************
Questions? Ask the ACAA Technical Agent