!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) @drepl If a formal parameter of a @fa is of a specific tagged type @i or of an anonymous access type designating a specific tagged type @i, @i is called a @i of the @fa. Similarly, if the result of a @fa for a function is of a specific tagged type @i or of an anonymous access type designating a specific tagged type @i, @i is called a controlling type of the @fa. A @fa shall have exactly one controlling type. @dby If a formal parameter of a @fa is of a specific tagged type @i or of an anonymous access type designating a specific tagged type @i, @i is called a @i of the @fa. Similarly, if the result of a @fa for a function is of a specific tagged type @i or of an anonymous access type designating a specific tagged type @i, @i is called a controlling type of the @fa. A @fa shall have exactly one controlling type, and that type shall not be incomplete. !corrigendum 13.14(10) @dinsa @xbullet of the @fa of an @fa.> @dinst @xbullet 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 @fa 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. :-) **************************************************************** 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. ****************************************************************