!standard 3.7.1(7/2) 07-07-25 AI05-0041-1/02 !standard 4.8(6/2) !class binding interpretation 07-03-27 !status work item 07-03-27 !status received 07-03-27 !priority Medium !difficulty Hard !qualifier Omission !subject Can a derived type be a partial view? !summary For the purposes of the rules for allowing allocated unconstrained objects, any ancestor that has a constrained partial view causes the rules to apply. !question It seems that the meaning of "partial view" in 3.7.1(7/2) is either obscure or too narrow. The ACATS 2.6 test contains an subtest like the following, for which the legality is not obvious. package B371001_1 is type T is private; private type T (D : Integer := 0) is null record; end B371001_1; package B371001_1.Child_2 is type T is new B371001_1.T; type Ptr1 is access all T; subtype S1A is Ptr1 (1); -- ERROR: [1] private subtype S1B is Ptr1 (2); -- ERROR: [2] type Ptr2 is access constant T; subtype S2 is Ptr2 (3); -- ERROR: [3] type Ptr3 is access T; P3 : Ptr3 (0); -- OK [4] end B371001_1.Child_2; The error at [1] occurs because only the partial view of B371001_1.T is visible in the visible part, and thus that is all that is derived. That view doesn't have discriminants, so the constraint is illegal. On the other hand, the private part has more visibility into the parent, and it can see that the type has discriminants. The errors at [2] and [3] are supposed to be because the type has defaulted discriminants; since that only applies to general access types, [4] is OK. However, that brings up the question of whether this type has a partial view. The test writer surely expected it to behave as if it does (by expecting some constraints to be able to see the discriminants, and thus be legal). In that case, the purpose of this rule (to prevent problems with discriminant constraints that point at unconstrained objects) would require the partial view rule to be enforced. What is the intent? Similar examples can be created using nested packages rather than child packages. !recommendation (See Summary.) !wording Change the new text for 3.3(23.9/3) from AI05-0008-1 as follows: * it is a dereference of a pool-specific access type, and there is no {ancestor of its type that has a} partial view [of its type] that is constrained. Change the new text for 3.3(23.10/3) from AI05-0008-1 as follows: For the purposes of determining within a generic body whether an object is known to be constrained, a subtype is not considered indefinite {and is considered to have a partial view that is constrained} if it is a descendant of an untagged generic formal derived type, nor is an access type considered pool-specific if it is a descendant of a formal access type. Change 3.7.1(7/2) as follows: ... However, in the case of an a general access subtype, a discriminant_constraint is illegal if the designated type has {an ancestor which has} a partial view that is constrained or, for a general access subtype, has default_expressions for its discriminants. In addition to the places where Legality Rules normally apply (see 12.3), these rules apply also in the private part of an instance of a generic unit. In a generic body, this rule is checked presuming all formal access types of the generic might be general access types, {all formal derived types have partial views that are constrained, }and all untagged discriminated formal types of the generic might have default_expressions for their discriminants. Change 4.8(6/2) as follows: ... If the designated type is composite, then the subtype of the created object is the designated subtype when the designated subtype is constrained or there is {an ancestor which has} a partial view of the designated type that is constrained; otherwise, the created object is constrained by its initial value ... !discussion Clearly, the type in the question has two views (7.3.1(4/1) explicitly states that additional information can be made visible for a derived type). So, the problem that 3.7.1(7/2) is intended to prevent is present for this type. It needs to apply. Defining this kind of type to be a partial view would surely work, but it would trigger a number of other rules (for instance, about completions, freezing, and representation clauses) which might be harmful. As such, doing that seems to be too dangerous to contemplate, especially for a Corrigendum level change. Changing 3.7.1(7/2) also requires changing the matching 4.8(6/2). This problem can get even more confused if there are multiple different partial views for a type. Consider: package P1 is type T1 (D : Integer := 10) is private; private type T1 (D : Integer := 10) is ... end P1; package P1.Child is type T2 is private; private type T2 is new T1; end P1.Child; Now T2 seems to have two partial views, one constrained and one unconstrained. In this case, the unconstrained partial view is unimportant, because the child can see the full view. But that isn't always true (especially for nested package). The wording needs to be careful to take cases like the above and like package P2 is type T1 (D : Integer := 10) is ... end P2; package P2.Child is type T2 is private; private type T2 is new T1; end P2.Child; into account. Surely the behavior of the above and of package P2.Child2 is type T3 is private; private type T3 (D : Integer := 10) is ... end P2.Child2; should not differ vis-a-vis the "constrained partial view" rules: an object of both T2 and T3 ought to be unconstrained if allocated with an allocator. It also needs to be the case that generic bodies assume that every derived formal type has such a partial view, so that they cannot cause this problem to reemerge. Similarly, we need to tweak the "known to be constrained" rules so that they take into account these changes, so that renames of discriminant-dependent components of potentially unconstrained objects are illegal. --!corrigendum 10.1.2(20/2) !ACATS test The test case in the question should be replaced in the ACATS 3.0 test B371001 (it has been removed until this AI is resolved). !appendix From: Randy Brukardt Date: Monday, March 23, 2007 7:07 PM It seems to me that the meaning of "partial view" in 3.7.1(7/2) [and I believe that there are similar rules elsewhere in the language] is either obscure or too narrow. I was updating the ACATS test for the Corrigendum version of this rule, and I ran across the following example: package B371001_1 is type T is private; private type T (D : Integer := 0) is null record; end B371001_1; package B371001_1.Child_2 is pragma Elaborate_Body; type T is new B371001_1.T; type Ptr1 is access all T; subtype S1A is Ptr1 (1); -- ERROR: [1] private subtype S1B is Ptr1 (2); -- ERROR: [2] type Ptr2 is access constant T; subtype S2 is Ptr2 (3); -- ERROR: [3] type Ptr3 is access T; P3 : Ptr3 (0); -- OK [4] end B371001_1.Child_2; The error at [1] occurs because only the partial view of B371001_1.T is visible in the visible part, and thus that is all that is derived. That view doesn't have discriminants, so the constraint is illegal. OTOH, the private part has more visibility into the parent, and it can see that the type has discriminants. The errors at [2] and [3] are supposed to be because the type has defaulted discriminants; since that only applies to general access types, [4] is OK. However, that brings up the question of whether this type has a partial view. The test writer surely expected it to behave as if it does (by expecting some constraints to be able to see the discriminants, and thus be legal). In that case, the purpose of this rule (to prevent problems with discriminant constraints that point at unconstrained objects) would require the partial view rule to be enforced. (Thanks to Pascal for this insight.) However, I'm now wondering if the whole idea is wrong here. This is a derived type, not a private type, and it doesn't get additional operations at a later point like a private type. (Right?). In that case, this type never would have (visible) discriminants, and all of the constraints are illegal. I know that in type extensions, you can lose the ability to access parent components that you can regain with a type conversion, and this seems to be a similar case. Probably the test ought to be structured with a new type in the private part: package B371001_1.Child_2 is pragma Elaborate_Body; type T is new B371001_1.T; type Ptr1 is access all T; subtype S1A is Ptr1 (1); -- ERROR: [1] private type TT is new B371001_1.T; type Ptr2 is access all TT; subtype S2 is Ptr2 (2); -- ERROR: [2] type Ptr2 is access constant TT; subtype S3 is Ptr3 (3); -- ERROR: [3] type Ptr4 is access TT; P4 : Ptr4 (0); -- OK [4] end B371001_1.Child_2; No one can see the constrained view of TT, so then the original intent holds, and there isn't a problem with the language wording (only the test). Thoughts? Which of these lines of logic is correct? **************************************************************** From: Gary Dismukes Date: Friday, March 23, 2007 7:08 PM > However, that brings up the question of whether this type has a partial > view. The test writer surely expected it to behave as if it does (by > expecting some constraints to be able to see the discriminants, and thus be > legal). In that case, the purpose of this rule (to prevent problems with > discriminant constraints that point at unconstrained objects) would require > the partial view rule to be enforced. (Thanks to Pascal for this insight.) Hmm, I'm not sure whether the RM defines such a derived type to be a partial view, but it seems that it should. (Not obvious to me how to derive that, so to speak, especially late on a Friday afternoon;). > However, I'm now wondering if the whole idea is wrong here. This is a > derived type, not a private type, and it doesn't get additional operations > at a later point like a private type. (Right?). In that case, this type > never would have (visible) discriminants, and all of the constraints are > illegal. I know that in type extensions, you can lose the ability to access > parent components that you can regain with a type conversion, and this seems > to be a similar case. No, derived types definitely do (or can) get additional operations at a later point, based on what's visible from the parent type. That's explicitly covered by 7.3(4/1). > Probably the test ought to be structured with a new type in the private > part: You could, though it doesn't seem that you should if the rules work out OK. Seems better to confirm whether the rules should apply to the test as written before revising the test, rather than skirting the issue. > package B371001_1.Child_2 is > pragma Elaborate_Body; > > type T is new B371001_1.T; > type Ptr1 is access all T; > subtype S1A is Ptr1 (1); -- ERROR: [1] > private > > type TT is new B371001_1.T; > type Ptr2 is access all TT; > subtype S2 is Ptr2 (2); -- ERROR: [2] > > type Ptr2 is access constant TT; > subtype S3 is Ptr3 (3); -- ERROR: [3] > > type Ptr4 is access TT; > P4 : Ptr4 (0); -- OK [4] > > end B371001_1.Child_2; > > > No one can see the constrained view of TT, so then the original intent > holds, and there isn't a problem with the language wording (only the test). The revised version certainly looks correct, but I think the original is as well (or at least I believe I can see an argument for that). > Thoughts? Which of these lines of logic is correct? See above. **************************************************************** From: Randy Brukardt Date: Friday, March 23, 2007 8:38 PM ... > Hmm, I'm not sure whether the RM defines such a derived type to be a partial > view, but it seems that it should. (Not obvious to me how to derive that, > so to speak, especially late on a Friday afternoon;). It certainly doesn't, because 7.3(4) defines partial view in terms of the syntax entities private_type_declaration and private_extension_declaration. So I don't think any amount of derivation (pun not intended - ED) is going to change that, on Friday evening, Monday morning, or any other day... > > However, I'm now wondering if the whole idea is wrong here. This is a > > derived type, not a private type, and it doesn't get additional operations > > at a later point like a private type. (Right?). In that case, this type > > never would have (visible) discriminants, and all of the constraints are > > illegal. I know that in type extensions, you can lose the ability to access > > parent components that you can regain with a type conversion, and this seems > > to be a similar case. > > No, derived types definitely do (or can) get additional operations at a later > point, based on what's visible from the parent type. That's explicitly covered > by 7.3(4/1). You mean 7.3.1(4/1), and you are right, I had forgotten about that. That means that the definition of partial view is suspect, surely in regards to 3.7.1(7/2), and probably other rules as well. > > Probably the test ought to be structured with a new type in the private > > part: > > You could, though it doesn't seem that you should if the rules > work out OK. > > Seems better to confirm whether the rules should apply to the test as > written before revising the test, rather than skirting the issue. Well, actually, the rule with the ACATS is that if there is enough doubt for it to go to the ARG, then we don't include the cases in the test suite. There is no reason to encourage implementers to do the something that might turn out to be the wrong thing, depending on what the ARG decides. I can try to guess what the ARG will decide, but I'm not always going to be right about that. I've got three tests already on a limbo list waiting for AI05-0008 and AI05-0028 to be completed, and this test can be added to it. Sounds like we need an AI to define that a type derived from a partial view is a partial view if there is a place within its immediate scope where the full view of the parent type is visible. Or something like that. Gotta worry about the effect on other rules that depend on partial views, though (should rep. clauses be illegal on such a type until the private part??? Etc.) **************************************************************** From: Pascal Leroy Date: Monday, March 26, 2007 1:49 AM > Sounds like we need an AI to define that a type derived from > a partial view is a partial view if there is a place within > its immediate scope where the full view of the parent type is > visible. Or something like that. Gotta worry about the effect > on other rules that depend on partial views, though (should > rep. clauses be illegal on such a type until the private part??? Etc.) I agree that we need an AI. My initial reaction when Randy asked that question in private email was, of course this type has a partial view: at the place of its declaration you don't know everything about it, and it's only in the private part that you find out what the type really is. So one hopes that, in the visible part, the type is considered composite (last sentence of 3.2(4.1/2)) even if the full view turns out to be an elementary type. On second thoughts it is really a strange partial view, though, because it doesn't "require completion" (in the sense of 3.11.1(1/1)) so it is "completely defined" at birth (see 3.11.1(8)). I wouldn't be surprised if some rules in the RM were written with the (apparently incorrect) assumption that "is a partial view" implies "is not completely defined". **************************************************************** From: Stephen W. Baird [private thread] Date: Monday, July 16, 2007 2:36 PM Here is an example to add to AI05-0041. This is an unconstrained-view-of-a-constrained-variable problem. Should this example compile successfully? If so, then what should happen when it executes? -------- procedure Derived_Ops_With_Access_Params is subtype Index is Integer range 0 .. 100; package Pkg1 is type T1 is private; private type T1 (Length : Index := 50) is record F : String (1 .. Length); end record; procedure Proc (X : access T1); package Pkg2 is type T2 is new T1; end Pkg2; end Pkg1; package body Pkg1 is procedure Proc (X : access T1) is begin X.all := (Index'Last, (others => Character'First)); end Proc; package body Pkg2 is type Ref is access T2; Ptr : Ref := new T2; X2 : T2 renames Ptr.all; -- Is X2 constrained? begin Proc (X2'Access); end Pkg2; end Pkg1; begin null; end Derived_Ops_With_Access_Params; **************************************************************** From: Randy Brukardt Date: Monday, July 16, 2007 3:46 PM I'm still getting over my recent bout of pneumonia, so I might not be making sense, but I don't see any (new) issue here. X2 should be whatever a similar X1 would be (if you did the same sequence of declarations for T1). [Unless you are claiming that is wrong too, in which case I wouldn't confuse it with the derived case. But I don't *think* that is what you are saying.] So, I would like to know what this example is supposed to illustrate that is not covered by the AI [else I have no idea of what to add to the AI]. Or, more generally, what's your point? (My brain is not able to guess right now.) **************************************************************** From: Stephen W. Baird Date: Monday, July 16, 2007 6:04 PM What's new here is the interaction with parameters of anonymous access types. You have an untagged unconstrained definite record type T1 which has a primitive operation with an "access T1" parameter. You declare a derived type T2, which is derived from T1. The two types disagree with respect to the "has a constrained partial view" property; in particular, T1 has the property and T2 does not. This is a recipe for trouble. When you call the derived subprogram, you can end up passing a reference to a constrained object to a subprogram which is expecting a reference to an unconstrained object (and which might assign a larger value to the designated object than it is capable of holding). Pkg1.Proc has a parameter X of type access T1. Since T1 has a constrained partial view, this means that X designates an unconstrained object (ignoring the X=null case). So there is no need for any checks in Pkg1.Proc when it assigns a big value to X.all. On the other hand, type T2 lacks a constrained partial view, so that allocated objects of type T2 are constrained. Thus, X2 is constrained. Note that the procedure call at the center of this whole mess, Proc (X2'Access); is a call to the derived subprogram Pkg2.Proc, not to Pkg1.Proc. Does this clarify matters? **************************************************************** From: Randy Brukardt Date: Monday, July 16, 2007 6:45 PM > What's new here is the interaction with parameters of anonymous access > types > > You have an untagged unconstrained definite record type T1 which has a > primitive operation with an "access T1" parameter. You declare a derived > type T2, which is derived from T1. The two types disagree with respect to the > "has a constrained partial view" property; in particular, T1 has the > property and T2 does not. But this is the point of the AI: T1 and T2 *cannot* disagree on this property, or all heck breaks loose. The original example has similar problems where you can get nonsense results because the language isn't clear whether T2 has a "constrained partial view". (I *do not* agree with you that it is clear that it does not have this property; derived types inherit properties, and in the case of child units, they have to inherit partial views as well or even Ada 95 is screwed up. This just seems to me to be a case where the wording is insufficiently clear...) > This is a recipe for trouble. Yes, of course, but as I said, that is the point of the original AI. There doesn't seem to be anything new here, other than another example of the problems caused if T2 does not have a "constrained partial view". > When you call the derived subprogram, you can end up passing a reference > to a constrained object to a subprogram which is expecting a reference to > an unconstrained object (and which might assign a larger value to the > designated object than it is capable of holding). > > Pkg1.Proc has a parameter X of type access T1. > Since T1 has a constrained partial view, this means that > X designates an unconstrained object (ignoring the X=null case). > > So there is no need for any checks in Pkg1.Proc when it assigns a > big value to X.all. > > On the other hand, type T2 lacks a constrained partial view, so > that allocated objects of type T2 are constrained. T2 cannot "lack a constrained partial view", or there is all manner of trouble (see the examples in the original AI). I agree there is a bug here, but it is the same one we're already talking about. > Thus, X2 is constrained. > > Note that the procedure call at the center of this whole mess, > Proc (X2'Access); > is a call to the derived subprogram Pkg2.Proc, not to Pkg1.Proc. > > Does this clarify matters? Yes, but I'm still confused as to why you think there is anything important different about this example from the one in the AI. The whole point of the AI is that types like T2 need to trigger the "constrained partial view" rules, or all kinds of anomolies are possible. I can believe that the AI doesn't say that very clearly, but I still don't know what is missing from the AI. Your original note claimed that this needed to be added to AI-41, and I still don't see why (other than in the !appendix as another curiousity). Parameter passing into derived subprograms (with any kind of parameter!) is why you get anomolies here. And in any case, there is no point of loading AIs down with dozens of examples; the only issue is to illustrate the problem. The original examples seem to do that just fine. What (if anything) am I missing?? **************************************************************** From: Pascal Leroy Date: Tuesday, July 17, 2007 2:07 AM Well, I am the one who suggested to Steve that he send this example to you. It took me some time to figure out that his example was actually similar to the one in AI05-0041. What I would like to see is a blurb in the discussion saying "while the original example involved a child unit, it is possible to run into the same problem with a nested package". I think this is worth pointing out because, while children and nested packages have analogies, the differences in visibility cause derived subprograms to pop up at different places and they don't always have the same problems. If you feel strongly otherwise I am not going to fight. Incidentally, I agree with you (and disagree with Steve): type T2 does have a partial view for the purpose of this rule. Note that we might not want to call it a partial view, though, because it would cause innumerable other rules to kick in. **************************************************************** From: Stephen W. Baird Date: Tuesday, July 17, 2007 3:30 PM Randy - You are right, using anonymous access parameters doesn't really change this into a new problem. Am I right in thinking that the presence or absence of an explicit partial view for a derived type should always be irrelevant for purposes of determining whether an allocated object is constrained? I understand that the AI hasn't been written yet; I'm asking about your understanding of the intent of the AI. I'm interested in the case where the parent type lacks a partial view and the derived type has one - the opposite of the case that is illustrated in the AI. **************************************************************** From: Randy Brukardt Date: Tuesday, July 17, 2007 4:40 PM > Am I right in thinking that the presence or absence of an explicit > partial view for a derived type should always be irrelevant for > purposes of determining whether an allocated object is constrained? I'm not sure. I'm pretty sure that that is the case when the derived type does not have a partial view. But... > I understand that the AI hasn't been written yet; I'm asking about > your understanding of the intent of the AI. I'm not sure I have one. I have an understanding of the problem, not so much of the solutions. I'm rather counting on Tucker to make it all better... ;-) > I'm interested in the case where the parent type lacks a partial > view and the derived type has one - the opposite of the case that > is illustrated in the AI. I don't think that you can ignore the partial view, if there is one, on the derived type. Otherwise, you can reintroduce the original problem. package P1 is type T1 (D : Integer := 10) is ... end P1; with P1; package P2 is type T2 is private; private type T2 is new T1; end P2; The full view of T2 has defaulted discriminants, and one would hope that allocated objects of T2 would be unconstrained (else we haven't fixed anything with these rules). There shouldn't be a difference semantically between the T2 given here and "type T2 (D : Integer := 0) is ...". So I think it does matter. What's even messier is if you add a partial view to T1 in the above example: package P1 is type T1 (D : Integer := 10) is private; private type T1 (D : Integer := 10) is ... end P1; with P1; package P2 is type T2 is private; private type T2 is new T1; end P2; Now T2 seems to have two partial views, one constrained and one unconstrained. Yikes! Oh, what a tangled web we weave... **************************************************************** From: Stephen W. Baird Date: Tuesday, July 17, 2007 6:12 PM This sounds desirable, but I'm worried about the reverse problem where thinking (statically) that allocated objects are constrained when (dynamically) they really aren't allows us to get in trouble. I'm thinking of something like a rename of a discriminant-dependent component of an allocated object within a derivable subprogram. On the other hand, perhaps 3.7.2(4) is broad enough to cover any such problems. I'll think about it. **************************************************************** From: Randy Brukardt Date: Tuesday, July 17, 2007 6:36 PM > This sounds desirable, but I'm worried about the reverse problem where > thinking (statically) that allocated objects are constrained > when (dynamically) they really aren't allows us to get in trouble. Is that possible? It seems that if an object of a type ought to be allocated unconstrained because of some view, then it should be allocated unconstrained (and everyone else should assume that). > I'm thinking of something like a rename of a discriminant-dependent component > of an allocated object within a derivable subprogram. Humm. > On the other hand, perhaps 3.7.2(4) is broad enough to cover any such > problems. Might be. It would seem to always apply to parameters (and when it doesn't, the new rule added by AI-0008 does). > I'll think about it. That scares me more than your original question. ;-) **************************************************************** From: Pascal Leroy Date: Wednesday, July 18, 2007 1:50 AM > Now T2 seems to have two partial views, one constrained and > one unconstrained. Yikes! From these examples, it seems to me that 3.7.1(7/2) should say something like: "... if the designated type has an ancestor which has a partial view that is constrained...". In other words, if there is a type to which you can convert that is private and doesn't expose defaulted discriminants, then the allocated objects are effectively unconstrained, and you don't want to be able to write an access constraint. It this fix works, it is much simpler than changing the meaning of "partial view". PS: I'm sure that Tuck won't like the wording above and will change it to something that reads better. **************************************************************** From: Stephen W Baird Date: Wednesday, July 18, 2007 12:27 PM > From these examples, it seems to me that 3.7.1(7/2) should say something > like: > > "... if the designated type has an ancestor which has a partial view that > is constrained...". This certainly sounds like an improvement on the current wording. As I mentioned to Randy, I am a little concerned that there may be problems in the opposite direction: > ... I'm worried about the reverse problem where > thinking (statically) that allocated objects are constrained > when (dynamically) they really aren't allows us to get in trouble. > > I'm thinking of something like a rename of a discriminant-dependent component > of an allocated object within a derivable subprogram. > > On the other hand, perhaps 3.7.2(4) is broad enough to cover any > such problems. I have not been able to construct any problems which 3.7.2(4) doesn't cover; on the other hand, relying on an erroneous execution rule isn't entirely satisfactory. As I mentioned to Pascal, there are also problems in this area with generic formal derived types (which can be solved with an assume-the-worst rule, or perhaps simply by invoking 3.7.2(4)): with Text_Io; procedure Constrained_By_Initial_Value is type T1 (Is_Float : Boolean := False) is record case Is_Float is when False => Int_Val : Integer; when True => Float_Val : Float; end case; end record; generic type D is new T1; package G is type Ref is access D; Ptr : Ref := new D; X : D renames Ptr.all; function Get return Integer; end G; package body G is Body_Rename : Integer renames X.Int_Val; function Get return Integer is begin return Body_Rename; end Get; end G; package Pkg is type T2 is private; private type T2 is new T1; package I is new G (T2); end Pkg; package body Pkg is begin I.X := (Is_Float => True, Float_Val => 1234.5); -- at this point, I.Body_Rename is a dangling rename Text_Io.Put_Line (Integer'Image (I.Get)); end Pkg; begin null; end Constrained_By_Initial_Value; **************************************************************** From: Randy Brukardt Date: Wednesday, July 25, 2007 7:14 PM ... >I have not been able to construct any problems which 3.7.2(4) doesn't cover; >on the other hand, relying on an erroneous execution rule isn't entirely >satisfactory. I agree. But we've already made massive changes to this area in AI05-0008. We might as well tweak those changes enough to cover this properly. I've done that in /02 of this AI. >As I mentioned to Pascal, there are also problems in this area with >generic formal derived types (which can be solved with an assume-the-worst >rule, or perhaps simply by invoking 3.7.2(4)): [This example came through in HTML, and that stripped all of the indentation...] ... >generic >type D is new T1; >package G is >type Ref is access D; >Ptr : Ref := new D; >X : D renames Ptr.all; ... > >package body G is >Body_Rename : Integer renames X.Int_Val; This is illegal with a tweaking of the existing (AI05-0008) assume-the-worst rule for renames ("known to be constrained"). We may need to discuss if this tweaking is too draconian, but I doubt it personally. ****************************************************************