Version 1.2 of ai05s/ai05-0296-1.txt

Unformatted version of ai05s/ai05-0296-1.txt version 1.2
Other versions for file ai05s/ai05-0296-1.txt

!standard 13.14(10)          12-03-14 AI05-0296-1/02
!standard 12.6(8.4/2)
!class Amendment 12-02-15
!status Amendment 2012 12-02-15
!status ARG Approved 7-0-3 12-02-25
!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 untagged incomplete type.
The controlling type of a formal abstract subprogram cannot be incomplete.
!problem
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.
!proposal
(See wording.)
!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 untagged incomplete type}; if the callable entity is an expression function, the expression of the expression function causes freezing.
Modify the last sentence of 12.6(8.4/2):
A formal_abstract_subprogram_declaration shall have excatly one controlling type{, and that type shall not be incomplete}.
!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 no calls on that subprogram are legal inside the generic (by 3.10.1(10/3)).
Unfortunately, we cannot allow this for formal tagged incomplete types, as some calls are allowed. The parameter of the tagged incomplete type would not be a problem, as it has to be passed by reference and thus we don't need to know the representation of the object. But the representation of other parameters may need to be known, and that cannot be guaranteed without freezing the profile of the subprogram. (It might be possible to use a more relaxed rule, but it is too hard to work out the exact details for a last-minute fix. We're rather be conservative.)
For example:
type Cnt_Type is range ...
generic type Inc is tagged; with function Is_Valid (O : Inc; Cnt : Cnt_Type) return Boolean; package Gen is procedure Do_It (O : Inc); end Gen;
package body Gen is procedure Do_It (O : Inc) is begin if Is_Valid (O, 1) then -- Legal, better be frozen. ... end if; end Do_It; end Gen;
Note that it only matters that the formal subprogram has one or more formal untagged incomplete parameters. As soon as there is one such parameter, the subprogram cannot be called and there is no need to require the profile to be frozen. The types of other parameters is irrelevant.
---
We purposely do not talk about whose "formal untagged incomplete type" is involved, because we want the rule to apply to any generic that can see the formal type. (Such a generic has to be nested or a child; other units cannot see the formal type, only the actual.)
For instance:
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); -- Do not freeze the profile of Bar.
---
In discussing the tagged cases above, another small hole was noted in the language definition. A formal abstract subprogram can be used to make a dispatching call, and we do not want to allow a dispatching call on a tagged incomplete type. The rules prevent any primitive operation from being declared on a type that could be called before it is completed, in order to prevent such calls. But a formal abstract subprogram is not primitive, so it doesn't trigger those rules.
Thus we add a few words to explicitly make this illegal. For example:
generic type T is tagged; with procedure Do_It (Obj : in T) is abstract; -- Now illegal. package Gen is procedure Really_Do_It (Obj : in T'Class); end Gen;
package body Gen is procedure Really_Do_It (Obj : in T'Class) is begin Do_It (Obj); -- Dispatching on tagged incomplete; no way!! end Really_Do_It;
end Gen;
!corrigendum 12.6(8.4/2)
Replace the paragraph:
If a formal parameter of a formal_abstract_subprogram_declaration is of a specific tagged type T or of an anonymous access type designating a specific tagged type T, T is called a controlling type of the formal_abstract_subprogram_declaration. Similarly, if the result of a formal_abstract_subprogram_declaration for a function is of a specific tagged type T or of an anonymous access type designating a specific tagged type T, T is called a controlling type of the formal_abstract_subprogram_declaration. A formal_abstract_subprogram_declaration shall have exactly one controlling type.
by:
If a formal parameter of a formal_abstract_subprogram_declaration is of a specific tagged type T or of an anonymous access type designating a specific tagged type T, T is called a controlling type of the formal_abstract_subprogram_declaration. Similarly, if the result of a formal_abstract_subprogram_declaration for a function is of a specific tagged type T or of an anonymous access type designating a specific tagged type T, T is called a controlling type of the formal_abstract_subprogram_declaration. A formal_abstract_subprogram_declaration shall have exactly one controlling type, and that type shall not be incomplete.
!corrigendum 13.14(10)
Insert after the paragraph:
the new paragraph:
!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. :-)

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

From: Tucker Taft
Sent: Thursday, March  1, 2012  9:41 AM

Ed and I have different memories of the decision regarding formal incomplete
types and freezing.  My memory is that we still allow *tagged* formal incomplete
types.  But when it comes to formal subprograms that reference the formal
incomplete type, we suppress freezing of the actual subprogram if it references
an *untagged* formal incomplete type, but we don't suppress freezing just
because it references a *tagged* formal incomplete type.

Ed remembers the discussion as concluding that we outlawed tagged formal
incomplete types completely.

Any other memories out there?

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

From: Edmond Schonberg
Sent: Thursday, March  1, 2012  10:08 AM

The original text of AI05-0296 states:

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.

So the change that Tuck proposes is "a formal incomplete TAGGED type of the same
generic" ?

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

From: Edmond Schonberg
Sent: Thursday, March  1, 2012  11:20 AM

I guess that should be "UNTAGGED", but then what if the subprogram has one of
each? Maybe it should say "the profile of the entity is frozen, except for
formals or result type of an untagged incomplete type of the same generic"

If 3.10.2 is the Heart of Darkness, what is the proper term for 13.14?

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

From: Randy Brukardt
Sent: Thursday, March  1, 2012  11:26 AM

That's what we decided.

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

From: Jeff Cousins
Sent: Thursday, March  1, 2012  11:39 AM

These few words are what I wrote at the time:

  "Only applies to untagged formal incomplete types."

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

From: Tucker Taft
Sent: Thursday, March  1, 2012  12:14 PM

>> So the change that Tuck proposes is "a formal incomplete UNTAGGED type of the same generic" ?
>
> I guess that should be "UNTAGGED", but then what if the subprogram has
> one of each? Maybe it should say "the profile of the entity is frozen,
> except for formals or result type of an untagged incomplete type of
> the same generic"

No, I would say either you freeze the actual subprogram or you don't.  There
seems no need for halfway freezing a subprogram, because you can't call it
halfway.

> If 3.10.2 is the Heart of Darkness, what is the proper term for 13.14?

Where hell freezes over... ;-)

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

From: Edmond Schonberg
Sent: Thursday, March  1, 2012  12:12 PM

> That's what we decided.

Alas, which "that"?

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

From: Randy Brukardt
Sent: Friday, March  2, 2012  12:34 PM

Sorry, I answered that from my phone and had limited time and energy to clarify
the response. And I didn't realize that you had it backwards...

(1) The word "untagged" is to be added to the wording as given in the AI. As
    Tucker noted, we can only avoid freezing if there is no possibility that the
    body will make a call on it.

(2) It's only the presence of "untagged incomplete" that triggers the rule, as
    that prevents any calls. The types of other parameters don't matter, even if
    tagged incomplete.

(3) I wonder if we have a tiny hole (near) here; the rules depend on the fact
    that there cannot be any primitive subprograms of a formal type to prevent
    any dispatching calls. But the purpose of abstract formal subprograms was to
    allow dispatching calls on them. Thus:

     generic
         type T is tagged;
         with procedure Do_It (Obj : in T) is abstract;
     package Gen is
         procedure Really_Do_It (Obj : in T'Class);
     end Gen;

     package body Gen is
        procedure Really_Do_It (Obj : in T'Class) is
        begin
              Do_It (Obj); -- Dispatching on tagged incomplete; no way!!
        end Really_Do_It;
    end Gen;

We don't allow dispaching calls on tagged incomplete (elsewhere) because the
location/representation of the tag is unknown for the incomplete type. We surely
don't want to allow that here!

We do that for normal tagged incomplete by banning the declaration of primitive
operations (3.10.1(9.3/2) and also because most such views aren't used in the
same scope as their declaration). I suspect we need a legality rule that the
controlling type of a formal abstract subprogram "shall not be incomplete".

Should I add that to this AI, or did I miss a rule somewhere else that would
make this call illegal??

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

From: Tucker Taft
Sent: Friday, March  2, 2012  1:41 PM

> Should I add that to this AI, or did I miss a rule somewhere else that
> would make this call illegal??

You should add it to the AI.

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

Questions? Ask the ACAA Technical Agent