!standard 12.5.1(21/3) 13-07-19 AI12-0030-1/06 !standard 13.13.2(49/2) !class binding interpretation 12-07-15 !status Corrigendum 1-2012 13-07-16 !status WG9 Approved 13-11-15 !status ARG Approved 8-0-0 13-06-15 !status work item 12-06-06 !status received 12-05-11 !priority Low !difficulty Medium !subject Formal derived types and stream attribute availability !summary For an untagged formal derived type, stream attributes (at runtime, in an instance) are those of the actual type. Those of the ancestor type do not reemerge. !question Consider this example: procedure Foo (S : not null access Ada.Streams.Root_Stream_Type'Class) is type Lim is limited null record; type Dlim is new Lim; -- does not inherit Lim'Read procedure Read_Lim (Stream : not null access Ada.Streams.Root_Stream_Type'Class; Item : out Lim) is null; for Lim'Read use Read_Lim; Lim_Var : Lim; Dlim_Var : Dlim; generic type Flim is new Lim; procedure G; procedure G is Flim_Var : Flim; begin Flim'Read (S, Flim_Var); -- Legal? (Yes.) end G; procedure I is new G (Dlim); begin Lim'Read (S, Lim_Var); Dlim'Read (S, Dlim_Var); -- Illegal. I; -- Legal, but raises Program_Error when called. end; Is the use of Flim'Read legal? (Yes.) If it is legal, are the dynamic semantics well defined? (Yes; when the procedure I invokes Dlim'Read, Program_Error is raised). Assuming that stream attribute availability is a "characteristic", it seems that 12.5.1(20/3) applies and the use within the generic body is legal. This means we need to define the meaning of the corresponding construct in the instance. The "even if it is never declared" wording in 12.5.1(21/3) handles a similar case, the case where a generic formal type promises a primitive operation that the corresponding actual type lacks. Do we need to add some analogous wording to handle this case? (No, but wording is added to ensure that the dynamic behavior of streaming attributes is defined in all cases including, for example, a task type with no user-defined streaming operations). !recommendation (See summary.) !wording Add after AARM 12.5.1(21.a/3): AARM To Be Honest: The availability of stream attributes is not formally a characteristic of a type, but it is still determined by the ancestor type for a formal derived type in the same way as the characteristics are. Availability is rechecked in the instance specification. Append to the end of 13.13.2(49/2): In addition to the places where Legality Rules normally apply (see 12.3), these rules also apply in the private part of an instance of a generic unit. [Editor's note: We have to use the plural version of this boilerplate text as there are two rules in this paragraph, and we want this to apply to both.] Unless inherited from a parent type, if any, for an untagged type having a task, protected, or explicitly limited record part, the default implementation of each of the Read, Write, Input, and Output attributes raises Program_Error and performs no other action. AARM note: It might seem that there is no need to specify the behavior of the default implementation of a streaming attribute of, for example, a task type because there is no way that it can be invoked. It is possible, however, to construct an example where such a stream attribute can be invoked. This involves using a formal untagged limited derived type for which some streaming attribute is available (because it was explicitly specified for the ancestor type) and a corresponding actual type for which the attribute is unspecified (because the derivation occurred before the aspect was specified for the ancestor type and the specification was therefore not inherited). !discussion The answer to the legality question is clear: characteristics of all sorts come from the ancestor type, not the actual type, so of course the attribute is legal and it will call the attribute of the ancestor type. However, the wording of the Standard is definitely not clear on this point. "Availability of stream attributes" is not a characteristic of a type (at least it is not listed in 3.4, which is the definition of a "characteristic"). So the existing rules don't cover this case. We could try to define "availability" as a "characteristic" (by adding a new bullet at 3.4(15), but that most likely would lead to conflicts as 7.3.1 defines how characteristics are inherited for private types, while 13.13.2 does so for "availability". These are similar, but probably not precisely the same, and we'd have to reword 13.13.2 in order to avoid confusion. Alternatively, we could just throw some text and add a new rule after 12.5.1(20/3) specifically to say that "availability of stream attributes" is the same as that of the ancestor type, and the stream attribute called is that of the ancestor type. But that just adds a lot of text to say something obvious. It's rare to see a derived untagged formal type, as such types can only match derived untagged types -- which is very limiting, especially given that untagged derivations themselves aren't that common (it's usually better to declare a new type). As such, it's not critical to have the Standard wording perfect in this area. So we simply add a To Be Honest note so there is no doubt of the intent. However, that leaves the dynamic semantics. It's clear from 13.1(11/3) that the stream attributes (which are operational aspects) used in the instance are those of the actual type. But in this case, the actual type has no stream attributes defined. Thus we have to define what the attributes do in this case. We've chosen to have them raise Program_Error. This means that the example given in the question compiles successfully and raises Program_Error at runtime. Raising Program_Error is fine - we just want the construct to be well defined and reasonably easy to implement. For an implemention which macro-expands instantiations, the Program_Error case is identifiable at compile-time. The "In addition" boilerplate isn't really necessary, but consider modifying the example so that the generic is a generic package, not a generic procedure, and the problematic call to Flim'Read is replaced with a call to Flim'Input in the private part of the generic. Without the boilerplate (and with the rest of the change) this raises Program_Error. With the boilerplate, this gets caught at compile time, which is preferable. !corrigendum 13.13.2(49/2) @drepl An @fa for one of the stream-oriented attributes is illegal unless the attribute is available at the place of the @fa. Furthermore, an @fa for @i'Input is illegal if @i is an abstract type. @dby An @fa for one of the stream-oriented attributes is illegal unless the attribute is available at the place of the @fa. Furthermore, an @fa for @i'Input is illegal if @i is an abstract type. In addition to the places where Legality Rules normally apply (see 12.3), these rules also apply in the private part of an instance of a generic unit. Unless inherited from a parent type, if any, for an untagged type having a task, protected, or explicitly limited record part, the default implementation of each of the Read, Write, Input, and Output attributes raises Program_Error and performs no other action. !ACATS test An ACATS C-test could be written that is similar to the example in the question. !appendix From: Steve Baird Sent: Friday, May 11, 2012 5:27 PM Consider this example: procedure Foo (S : not null access Ada.Streams.Root_Stream_Type'Class) is type Lim is limited null record; type Dlim is new Lim; -- does not inherit Lim'Read procedure Read_Lim (Stream : not null access Ada.Streams.Root_Stream_Type'Class; Item : out Lim) is null; for Lim'Read use Read_Lim; Lim_Var : Lim; Dlim_Var : Dlim; generic type Flim is new Lim; procedure G; procedure G is Flim_Var : Flim; begin Flim'Read (S, Flim_Var); -- legal? end G; procedure I is new G (Dlim); begin Lim'Read (S, Lim_Var); Dlim'Read (S, Dlim_Var); -- illegal I; end; Is the use of Flim'Read legal? Should it be? If it is legal, are the dynamic semantics well defined? Assuming that attribute availability is a "characteristic", it seems that 12.5.1(20/3) applies and the use within the generic body is legal. This means we need to define the meaning of the corresponding construct in the instance. The "even if it is never declared" wording in 12.5.1(21/3) handles a similar case, the case where a generic formal type promises a primitive operation that the corresponding actual type lacks. We may need to add some analogous wording to handle this case (or at least a TBH note if this seems like too much of a corner case). **************************************************************** From: Tucker Taft Sent: Sunday, May 13, 2012 10:08 PM > generic > type Flim is new Lim; > procedure G; > procedure G is > Flim_Var : Flim; > begin > Flim'Read (S, Flim_Var); -- legal? Yes. > end G; > procedure I is new G (Dlim); > begin > Lim'Read (S, Lim_Var); > Dlim'Read (S, Dlim_Var); -- illegal True. > I; > end; > > Is the use of Flim'Read legal? Should it be? Yes, just like all operations for non-tagged types. > If it is legal, are the dynamic semantics well defined? Yes, since the semantics are always to use the operation of the specified ancestor type if non-tagged. We have now made "=" a special case, but all other operations come from the ancestor type. > Assuming that attribute availability is a "characteristic", it seems > that 12.5.1(20/3) applies and the use within the generic body is > legal. This means we need to define the meaning of the corresponding > construct in the instance. > > The "even if it is never declared" wording in 12.5.1(21/3) handles a > similar case, the case where a generic formal type promises a > primitive operation that the corresponding actual type lacks. We may > need to add some analogous wording to handle this case (or at least a > TBH note if this seems like too much of a corner case). I don't really see this as a very special case. For non-tagged types, unless you explicitly import the operation (or it is equality), you always use the operations of the specified ancestor, even if they are overridden in the actual type. **************************************************************** From: Steve Baird Sent: Monday, May 14, 2012 12:31 PM > I don't really see this as a very special case. > For non-tagged types, unless you explicitly import the operation (or > it is equality), you always use the operations of the specified > ancestor, even if they are overridden in the actual type. If I understand you correctly, you are talking about the following 12.5.1(21/3) rule: In an instance, the copy of such an implicit declaration of a primitive subprogram of a formal derived type declares a view of the corresponding primitive subprogram of the ancestor or progenitor of the formal derived type, even if this primitive has been overridden for the actual type and even if it is never declared for the actual type. and (in passing) the 3.4(17/2) rule that begins For each user-defined primitive subprogram (other than a user-defined equality operator - see below) of the parent type ... Note that these rules talk about primitive subprograms, not primitive operations. I think that means that they don't apply to the question I raised. Note that it wouldn't change the example in any essential way if we introduced a nested package so that the procedure Read_Lim is not a primitive operation of type Lim, as in type Lim is limited null record; type Dlim is new Lim; -- does not inherit Lim'Read package Prevent_Primitiveness is procedure Read_Lim (Stream : not null access Ada.Streams.Root_Stream_Type'Class; Item : out Lim) is null; end Prevent_Primitiveness; for Lim'Read use Prevent_Primitiveness.Read_Lim; This is not a question about primitive subprograms. **************************************************************** From: Tucker Taft Sent: Monday, May 14, 2012 12:56 PM > ... > Note that these rules talk about primitive subprograms, not primitive > operations. I think that means that they don't apply to the question I > raised. You are right in the letter of the law, but I think the intent was to include all overrideable operations. Note that the streaming operations of tagged types are considered dispatching operations, so that would mean that we are already treating them pretty similarly to primitives. In general, we have not presumed any significant "descriptor" needed to accompany formal derived non-tagged types (presuming you have some amount of sharing), since essentially all of the properties inside the generic are determined by the specified ancestor type. I agree we should clarify this, but I have no doubt which way it should be clarified. Your example is a good indication of why for non-tagged types we should carry this same principle through to the streaming operations, and use the operations of the specified ancestor type inside the generic. > ... This is not a question about primitive subprograms. Agreed, but it is a question about something which is the "moral equivalent" of a primitive, and I believe we should clarify the RM that they should follow the same rules. **************************************************************** From: Steve Baird Sent: Monday, May 14, 2012 1:24 PM Sounds good to me. **************************************************************** From: Steve Baird Sent: Friday, June 29, 2012 6:51 PM Randy Brukardt wrote (in private communication): > The model Tucker gave for untagged derived types is crystal-clear: all > non-representation properties come from the ancestor, period, and > nothing comes from the actual. That includes the routines that are > called at runtime. Randy sent me a proposed writeup for this AI which captured the above idea. Recall that the example that led to this AI involves a formal untagged derived type whose ancestor type has streaming attributes available and a corresponding actual type which does not have streaming attributes available. In the generic body, we perform streaming operations. At runtime, what is supposed to happen? The model Randy wrote up answers this question by saying that we use the streaming attributes of the ancestor type unconditionally, and the streaming attributes of the actual are ignored. Tuck described this model in a mail message of May 14, to which my reply was "Sounds good to me". Now I'm not so sure. Consider the following: type T1 is record ... ; -- untagged type T2 is new T; for T2'Read use ...; for T2'Write use ...; generic type Formal_Private is private; package G1 is ...; end; package body G1 is begin -- do some Formal_Private streaming stuff end; generic type Formal_Derived is new T1; package G2 is ...; end; package body G2 is begin -- do some Formal_Derived streaming stuff end; package I1 is new G1 (T2); package I2 is new G2 (T2); Do the two instances execute the same streaming ops? It would seem odd to me if they didn't. What streaming ops do we get if G2 instantiates G1 with Formal_Private => Formal_Derived ? I don't remember the details of the discussion in Stockholm, but neither Randy nor I remember any discussion of this aspect of the question. Other alternatives for resolving the original question that led to this AI include: 1) A legality rule - if streaming attributes are available for a formal derived type, then they must be available for the actual. 2) A conditional rule - use the streaming attributes of the actual, unless they don't exist in which case use the streaming attributes of the ancestor type (or perhaps raise P_E). I'm leaning towards a "use the streaming attributes of the actual" model combined with rule #1, which guarantees that those streaming attributes exist. The best argument for the model Tuck proposed, I think, is that this is a completely unimportant corner case and therefore any choice is ok as long as it is well-defined and we should go with whatever is simplest. I could continue to support Tuck's model on these grounds. Opinions? **************************************************************** From: Tucker Taft Sent: Saturday, June 30, 2012 6:19 AM > package I1 is new G1 (T2); > package I2 is new G2 (T2); > > Do the two instances execute the same streaming ops? G1 uses the predefined streaming attributes. Predefined operations reemerge in generics for untagged (non-derived) formal types (except for record equality as of Ada 2012). G2 uses T1's streaming attributes. The ancestor type's operations reemerge in generics for untagged formal derived types (again, except for record equality, as of Ada 2012). So long as T1's streaming attributes are predefined, then G1 and G2 use the same attributes. > It would seem odd to me if they didn't. I agree, presuming T1's streaming attributes are predefined. > What streaming ops do we get if G2 instantiates G1 with Formal_Private > => Formal_Derived The predefined attributes, so if T1 has user-defined streaming attributes, these are not used inside the nested instantiation of G1. > ? > > I don't remember the details of the discussion in Stockholm, but > neither Randy nor I remember any discussion of this aspect of the > question. Formal private is essentially equivalent to formal derived where the ancestor is a type with only predefined operations (again, modulo user-defined equality on records). I also think you need to consider this same question for all untagged formal generic types (formal integer, formal array, etc.). I believe in all these cases the predefined streaming attributes should reemerge, just like the predefined "+", etc. > > Other alternatives for resolving the original question that led to > this AI include: > > 1) A legality rule - if streaming attributes are available for a > formal derived type, then they must be available for the actual. > > 2) A conditional rule - use the streaming attributes of the actual, > unless they don't exist in which case use the streaming attributes of > the ancestor type (or perhaps raise P_E). > > I'm leaning towards a "use the streaming attributes of the actual" > model combined with rule #1, which guarantees that those streaming > attributes exist. It seems odd to me that predefined "+" reemerges on a formal integer type but predefined streaming does *not* reemerge. > The best argument for the model Tuck proposed, I think, is that this > is a completely unimportant corner case and therefore any choice is ok > as long as it is well-defined and we should go with whatever is > simplest. I could continue to support Tuck's model on these grounds. I don't believe it is a "corner case" argument. I believe it is for consistency with all of the other predefined operations, which *do* reemerge in a generic unless the formal type is tagged. > Opinions? See above. **************************************************************** From: Tucker Taft Sent: Saturday, June 30, 2012 7:08 AM I can also construct a counter-argument (here comes TT-2): Because streaming attributes "compose" even for untagged types, that is, the "predefined" streaming attribute for a record or array type is produced by composing the user-defined streaming attributes of its component types, the user-defined streaming attributes should be carried through into generics. This seems pretty clear if the generic spec declares a record type whose components are formal types. One would certainly expect that outside the generic, this newly constructed record type's streaming attribute would be composed out of the user-defined streaming attributes of its component types. It would be pretty weird if this record type had different behavior than an otherwise identical record type declared outside of the generic instance. Given this automatic composability, it seems that we need to carry streaming attributes into generics. So now we are back to needing rules to deal with formal limited derived types. Groan... **************************************************************** From: Steve Baird Sent: Monday, July 2, 2012 12:51 PM > I don't believe it is a "corner case" argument. I believe it is for > consistency with all of the other predefined operations, which > *do* reemerge in a generic unless the formal type is tagged. I agree with you here (and with TT-2 later). > I can also construct a counter-argument (here comes TT-2): As you point out, if we are going to be consistent then this affects more than just the formal untagged derived case. I think this leads to a fairly strong compatibility-with-existing-practice argument for TT-2's position. For at least the GNAT implementation (and, I believe, the IBM/Rational compiler), predefined streaming attributes do not currently reemerge. My guess is that this is the case for most any implementation which does not use generic code sharing, since this is what naturally falls out of the macro-expansion model. The xample below, compiled with the Gnat compiler, prints User-defined stream attrs were invoked IMO, this is a good thing and we want a language definition that allows/requires this behavior (which the proposed model does not). Incidentally, package Simple_Streams is just a simpler alternative to Ada.Streams.Stream_IO that I tend to use when investigating streaming issues. Nothing special about it. with Text_IO; with Ada.Streams; with Simple_Streams; procedure formal_type_streaming is type Vector is array (Positive range <>) of Integer; procedure Output (S : not null access Ada.Streams.Root_Stream_Type'Class; V : Vector); function Input (S : not null access Ada.Streams.Root_Stream_Type'Class) return Vector; for Vector'Output use Output; for Vector'Input use Input; procedure Output (S : not null access Ada.Streams.Root_Stream_Type'Class; V : Vector) is begin -- only write out length, not both bounds Integer'Output (S, V'Length); for I in V'Range loop Integer'Output (S, V(I)); end loop; end; function Input (S : not null access Ada.Streams.Root_Stream_Type'Class) return Vector is -- low bound of result is always 1 Result : Vector (1 .. Integer'Input (S)); begin Vector'Read (S, Result); return Result; end; generic type Formal_Vector is array (Positive range <>) of Integer; function Ident (V : Formal_Vector) return Formal_Vector; function Ident (V : Formal_Vector) return Formal_Vector is S: aliased Simple_Streams.Stream_Type; begin Formal_Vector'Output (S'access, V); return Formal_Vector'Input (S'access); end; function Ident_Inst is new Ident (Formal_Vector => Vector); X : constant Vector (2 .. 4) := (2, 3, 4); Y : constant Vector := Ident_Inst (X); begin pragma Assert (X = Y); if Y'First = 1 then Text_IO.Put_Line ("User-defined stream attrs were invoked"); else Text_IO.Put_Line ("Predefined stream attrs were invoked"); end if; end; with Ada.Streams; use Ada.Streams; package Simple_Streams is type Stream_Type is new Root_Stream_Type with private; private subtype Buffer_Index is Stream_Element_Offset range 1 .. 1024; subtype Buffer_Length is Stream_Element_Count range 0 .. Buffer_Index'Last; type Stream_Type is new Root_Stream_Type with record Buffer : Stream_Element_Array (Buffer_Index); Length : Buffer_Length := 0; end record; overriding procedure Read( Stream : in out Stream_Type; Item : out Stream_Element_Array; Last : out Stream_Element_Offset); overriding procedure Write( Stream : in out Stream_Type; Item : in Stream_Element_Array); end Simple_Streams; package body Simple_Streams is procedure Read( Stream : in out Stream_Type; Item : out Stream_Element_Array; Last : out Stream_Element_Offset) is begin if Item'Length <= Stream.Length then Item := Stream.Buffer (1 .. Item'Length); Last := Item'Last; Stream.Length := Stream.Length - Item'Length; Stream.Buffer (1 .. Stream.Length) := Stream.Buffer (1 + Item'Length .. Stream.Length + Item'Length); else Item (Item'First .. Item'First + Stream.Length - 1) := Stream.Buffer (1 .. Stream.Length); Last := Item'First + Stream.Length - 1; Stream.Length := 0; end if; end Read; procedure Write( Stream : in out Stream_Type; Item : in Stream_Element_Array) is begin Stream.Length := Stream.Length + Item'Length; Stream.Buffer (Stream.Length + 1 - Item'Length .. Stream.Length) := Item; end Write; end Simple_Streams; **************************************************************** From: Randy Brukardt Sent: Monday, July 2, 2012 3:19 PM ... > I think this leads to a fairly strong > compatibility-with-existing-practice argument for TT-2's position. > > For at least the GNAT implementation (and, I believe, the IBM/Rational > compiler), predefined streaming attributes do not currently reemerge. > My guess is that this is the case for most any implementation which > does not use generic code sharing, since this is what naturally falls > out of the macro-expansion model. I haven't encountered any compilers for which stream attributes did not dynamically call the user-defined attributes. I certainly thought that was the model for Ada. It's also a usability issue. You wouldn't be able to usefully stream anything from inside a generic unit if the predefined attributes reemerged. (You'd get useless results if any access types are involved.) More generally, reemergence is almost always a problem (given that it means that a generic unit works differently than the same unit created by hand using textual substitution). We ought to use it only where it is necessary for compatibility, and nowhere else. In any case, the fact that Tucker is even willing to make an argument for reemergence suggests that the entire model of stream attributes in generics is ill-defined. So just using a Ramification to solve this is a bad idea - we need to correct the wording so it is crystal-clear as to what happens. ... > Incidentally, package Simple_Streams is just a simpler alternative to > Ada.Streams.Stream_IO that I tend to use when investigating streaming > issues. > Nothing special about it. Actually, this looks very similar to Claw.Marshalling (which we use to allow items to be streamed into and out of the Windows clipboard and registry). It's something that is often needed and probably ought to be language-defined. (The topic has informally come up before, I believe, but never formally.) [The only significant difference here (other than naming) is that Claw.Marshalling uses a discriminant to specify the buffer size, since this is definitely a case where one size does not fit all. Well, and Claw.Marshalling also has Length and Clear operations. The implementation also uses buffer chunks to allow the buffer to grow beyond the initial size, but that's probably overkill.] ... > with Ada.Streams; > use Ada.Streams; > package Simple_Streams is > type Stream_Type is new Root_Stream_Type with private; private > subtype Buffer_Index is > Stream_Element_Offset range 1 .. 1024; > subtype Buffer_Length is > Stream_Element_Count range 0 .. Buffer_Index'Last; > > type Stream_Type is new Root_Stream_Type with > record > Buffer : Stream_Element_Array (Buffer_Index); > Length : Buffer_Length := 0; > end record; > > overriding > procedure Read( > Stream : in out Stream_Type; > Item : out Stream_Element_Array; > Last : out Stream_Element_Offset); > > overriding > procedure Write( > Stream : in out Stream_Type; > Item : in Stream_Element_Array); > > end Simple_Streams; **************************************************************** From: Jeff Cousins Sent: Wednesday, July 4, 2012 6:24 AM Wearing my user's hat, re-emergence is abhorrent and breaks the language design principle of "no surprises". **************************************************************** From: Randy Brukardt Sent: Tuesday, July 17, 2012 6:17 PM FYI: > I can also construct a counter-argument (here comes TT-2): > > Because streaming attributes "compose" even for untagged types, that > is, the "predefined" streaming attribute for a record or array type is > produced by composing the user-defined streaming attributes of its > component types, the user-defined streaming attributes should be > carried through into generics. > > This seems pretty clear if the generic spec declares a record type > whose components are formal types. One would certainly expect that > outside the generic, this newly constructed record type's streaming > attribute would be composed out of the user-defined streaming > attributes of its component types. > It would be pretty weird if this record type had different behavior > than an otherwise identical record type declared outside of the > generic instance. > > Given this automatic composability, it seems that we need to carry > streaming attributes into generics. Steve privately pointed out that we all had missed the first sentence of 13.1(11/3): Operational and representation aspects of a generic formal parameter are the same as those of the actual. Stream attributes are operational aspects, thus the actual's values are always used. QED. This matches with existing practice, as well. Steve has already written this AI up, so we don't need to discuss it further, I just wanted to put on the record that TT-1 was not supported by the Standard. **************************************************************** Summary of private mail thread, July 19th, 2013 Randy Brukardt asks: The wording we added in AI12-0030-1 (will be 13.13.2(49.1/4) starts: "For an untagged nonderived type having a task, protected, or explicitly limited record part ..." The example of the question of the AI starts: type Lim is limited null record; type Dlim is new Lim; -- does not inherit Lim'Read procedure Read_Lim (Stream : not null access Ada.Streams.Root_Stream_Type'Class; Item : out Lim) is null; for Lim'Read use Read_Lim; Note that type Dlim, for which we need this rule to define the stream attributes runtime behavior, is a derived type. Thus the new rule does not apply to Dlim. OK, perhaps inheritance will fix us. Let's look at 13.13.2(8.1/3): "For an untagged derived type, the Write (resp. Read) attribute is inherited according to the rules given in 13.1 if the attribute is specified and available for the parent type at the point where T is declared." (T here refers to the type of the attribute.) So this doesn't apply, either, as the attribute is not specified at the point where T is declared. So precisely how does this wording make the example in the question raise Program_Error? I'd say that it does not. Steve Baird replies: > ... Thus the new rule does not apply to Dlim. Ok, no problem so far. This is as I would expect. ... > So this doesn't apply, either, as the attribute is not specified at > the point where T is declared. Oh ugh. I thought the "raise P_E" streaming attributes would be inherited. That's how this case is supposed to work. [Followed a by a long diversion into a solution that doesn't work as proposed.] Randy: We could patch up the original rule by modifying the new 13.13.2(49.1/4) rule as follows: "{Unless inherited from a parent type, if any, for}[For] an untagged [nonderived] type having a task, protected, or explicitly limited record part, the default implementation of each of the Read, Write, Input, and Output attributes raises Program_Error and performs no other action." I would have preferred that you have simply followed the existing pattern and somehow worked your new rule in as what the default implementation is defined to do (that is, as new bullets following 13.13.2(9.1/3) and 13.13.2(27/3)), but that would be more than an Editorial Review fix. Tucker Taft: Seems fine to me. Steve: Ditto. Jeff Cousins: I'm not sure about this one, but changing something in 13.3.2 seems preferable to changing 13.1. [So this was done in version /06 of the AI - Editor.] ****************************************************************