!standard 13.13.1 (00) 00-11-14 AI95-00195/07 !class binding interpretation 98-03-27 !status work item 98-04-04 !status received 98-03-27 !reference AI95-00108 !reference AI95-00145 !priority High !difficulty Hard !subject Streams !summary 1 - When the predefined Input attribute creates an object, this object undergoes default initialization and finalization. 2 - For the purposes of checking legality rules, it is necessary to determine whether a stream-oriented attribute has been specified for a limited type (RM95 13.13.2(36)). This is done by applying the normal visibility rules to the attribute_definition_clause. A stream-oriented attribute may be specified before the type has been fully defined. 3 - For a limited tagged type T, if Read is specified for an ancestor type of T, then T'Class'Read, T'Input, and T'Class'Input can be called, even if these are not specified. Similarly, if Write is specified for an ancestor type of T, then T'Class'Write, T'Output, and T'Class'Output can be called. 4 - In the profiles of the stream-oriented attributes, the notation "italicized T" refers to the base subtype for a scalar type, and to the first subtype otherwise. 5 - In an attribute_definition_clause for a stream-oriented attribute, the name shall not denote an abstract subprogram. 6 - The predefined Read attribute for composite types with defaulted discriminants must ensure that, if exceptions are raised by the Read attribute for some discriminant, the discriminants of the actual object passed to Read are not modified. This may require the creation of an anonymous object, which undergoes initialization and finalization. 7 -The predefined Read attribute for composite types with defaulted discriminants must raise Constraint_Error if the discriminants found in the stream differ from those of the actual parameter to Read, and this parameter is constrained. 8 - If S is a subtype of an abstract type, an attribute_reference for S'Input is illegal unless this attribute has been specified by an attribute_definition_clause. 9 - The number of calls performed by the predefined implementation of the stream-oriented attributes to the Read and Write operations of the stream type is unspecified (an implementation may take advantage of this rule to perform internal buffering). However, all the calls to Read and Write needed to implement a top-level invocation of a stream-oriented attribute must take place before this top-level invocation returns. !question 1 - RM95 13.13.2(27) states that S'Input "creates an object (with the bounds or discriminants, if any, taken from the stream), initializes it with S'Read, and returns the value of the object." Does the verb "initialize" in this sentence refer to the entire initialization process mentioned in RM95 3.3.1(18) and 7.6(10)? (yes) In particular, if S is a controlled subtype, or if it contains controlled components, is the Initialize subprogram called? (yes) Is the Finalize subprogram called when the intermediate object is finalized? (yes) For a record type whose components have initial values, are these values evaluated? (yes) 2 - RM95 13.13.2(36) states that "an attribute_reference for one of these attributes is illegal if the type is limited, unless the attribute has been specified by an attribute definition clause." If some stream-oriented attribute has been specified in a private part, and we are at a point that doesn't have visibility over that private part, is a reference to the attribute legal? (no) 3 - Let T be a limited type with an attribute_definition_clause for attribute Read. Is a usage of T'Class'Read legal? (yes) 4 - The definition of the profiles of S'Read, S'Write, S'Input and S'Output given in RM95 13.13.2 uses the notation "italicized T" for the type of the Item parameter. What is the meaning of "italicized T" in this context? (T'Base for scalars, first subtype otherwise.) 5 - In an attribute_definition_clause for a stream attribute, is it legal to give a name that denotes an abstract subprogram? (no) 6 - RM95 13.13.2(9) states that for a record type, the predefined S'Read reads the components in positional aggregate order. However, the language doesn't seem to specify what happens when exceptions are raised by the calls to the Read attribute for the components. Consider for example the following type declarations: type T1 is range ...; type T2 is range ...; type R (D1 : T1 := ...; D2 : T2 := ...) is record ... end record; Say that attributes_definition_clauses have been given for T1'Read and T2'Read, and consider a call to R'Read. Assume that, during this call, an exception is raised by T2'Read. Is the discriminant X.D1 modified? (no) 7 - Consider a call to T'Read where T is a type with defaulted discriminants. If the discriminants found in the stream have values different from those of the discriminants of the object passed to T'Read for the Item parameter, and that object is constrained, is Constraint_Error raised? (yes) 8 - If T is an abstract type, is the function T'Input abstract? (no, but it cannot be called) 9 - RM95 13.13.1(1) states that "T'Read and T'Write make dispatching calls on the Read and Write procedures of the type Root_Stream_Type." Is the number of those calls specified? (no) !recommendation (See summary.) !wording (See summary.) !discussion 1 - It seems logical to make Input follow as closely as possible the semantics of code that could be written by a user, so the implementation of S'Input should obtain the discriminants or bounds from the stream, and then declare an object with appropriate discriminants or bounds, as in: declare Anon : S (); begin S'Read (..., Anon); end; Accordingly, the initialization described in RM95 3.3.1(8-9) and RM95 7.6(10) takes place when Anon is declared, and the finalization described in RM95 7.6.1 takes place when Anon disappears (and before returning from the call to S'Input). Note that as part of initialization, compiler-specific fields are initialized as required by the implementation (and as permitted by AARM 3.3.1(14.a)). 2 - Consider for example: package P is type T is limited private; private type T is new Boolean; procedure Read (Stream : ...; Item : out T); for T'Read use Read; end P; with P; procedure Q is X : P.T; begin P.T'Read (..., X); -- Illegal? end Q; The call to P.T'Read is illegal, because Q doesn't have visibility over the private part of P, which contains the attribute_definition_clause for attribute Read. On the other hand, at a place that has visibility over the private part of P (and that comes after the attribute_definition_clause) a reference to T'Read is legal. This rule is necessary to preserve the privacy of private types. Note that it is the location of the attribute_definition_clause that counts, not that of the subprogram specified in that clause. Thus, if the procedure Read above were moved to the visible part of P, a reference to P.T'Read would still be illegal (but a reference to P.Read wouldn't). 3 - The rules given in 13.13.2(36) (as amended by 8652/0040 [AI-00108]), say that "All nonlimited types have default implementations for these operations." However, many limited types also have default implementations for these operations; yet they may not be called. The rules given in AI-108 insure that if a limited tagged ancestor type has a valid, callable 'Read attribute, then all of types derived from that ancestor also have a valid 'Read attribute. Therefore, dispatching to the operation is safe, and there is no need to prohibit calls to T'Class'Read in that case. Similar logic applies to 'Write. Similarly, the default implementation of T'Input is defined in terms of T'Read. If T'Read is callable, then the implementation of T'Input is well-defined as well, and there is no reason to prohibit calls. A similar argument applies to T'Output when T'Write is callable. Finally, if T'Input is callable, then T'Class'Input is well-defined, and also should be callable. Therefore, 13.13.2(36) should allow calling any of these attributes if the appropriate 'Read (or 'Write) is defined. 4 - AI95-00145 specifies the meaning of the notation "italicized T" for operators as follows: "The italicized T shown in the definitions of predefined operators means: - T'Base, for scalars - the first subtype, for tagged types - the type without any constraint, in other cases" In the case of stream-oriented attributes the notation "italicized T" must be consistent with the parameter subtype required for attribute_definition_clauses. If we chose the same rule as for operators, we would have a discrepancy in the case of constrained untagged types, and this would unnecessarily complicate the static and dynamic semantics. When one of the stream-oriented attributes is specified by an attribute definition clause, RM95 13.13.2(36) states that "the subtype of the Item parameter shall be the base subtype if scalar, and the first subtype otherwise." Therefore, in the parameter profiles for the stream-oriented attributes in section 13.13, the notation "italicized T" means: - T'Base, for scalars - the first subtype, in other cases 5 - Obviously it should not be possible to perform a non-dispatching call to an abstract subprogram (stream-oriented attributes are always called in a non-dispatching manner). Therefore, we have two options: - Make the attribute_definition_clause illegal. - Make the calls (explicit or implicit) illegal. The second option is a significant implementation burden, and allowing the attribute_definition_clause only to reject all calls doesn't seem to do any good. That's why the first option was preferred. 6 - The problem mentioned in the question only exists for a type R that is passed by-reference to R'Read: obviously if the type is passed by copy, an exception raised by R'Read cannot affect the original object. In the case of a type which is passed by-reference, we must consider two cases: - If the exception in T2'Read is due to the failure of some language-defined check, RM95 11.6(6) explains that the object or its parts may become abnormal, so we don't have to specify what happens to the discriminants. - If the exception in T2'Read is due to an explicit raise, the object obviously doesn't become abnormal, and therefore we must preserve the integrity of its discriminants. In other words, either all discriminants are updated (if the calls to the Read attributes for the discriminants were successful) or none (if any call to a Read attribute for a discriminant raised an exception). This model requires an implementation to read the discriminants, create an anonymous object using the given discriminants and assign that object to the formal parameter Item. The normal initialization and finalization take place for this anonymous object. Strictly speaking, the use of an anonymous object is only required for a type with defaulted discriminants which is passed by-reference, if the actual parameter of Read is not constrained. However, an implementation is free to use anonymous objects in other cases. Use of an anonymous object is only required for discriminants. An implementation is free to read the other components directly in the Item parameter. For example, if we change the original example as follows: type R is record C1 : T1; C2 : T2; end record; then if T2'Read raises an exception when reading component C2, it is unspecified if C1 is modified or not. 7 - When the type has defaulted discriminants, the predefined Read attribute must read them from the stream. It can be the case that the actual object passed to Read is constrained. In this case, the discriminants found in the stream may or may not match those of the actual. If they don't match, Constraint_Error is raised, and this is a Discriminant_Check. It is unspecified whether this effect is achieved by assigning a temporary object, as explained in #6 above, or by other means. 8 - If T is an abstract type, calling the function T'Input would effectively create an object whose tag designates T, which is absurd. We could decide that T'Input is abstract, but it seems simpler to say that any attribute_reference for this attribute is illegal, by analogy with the rule stated in RM95 13.13.2(36) for limited types. 9 - Surely the user could count the calls to the Read and Write operations for the stream type by writing a perverse implementation of these operations, but it seems that performance should have precedence: for the predefined implementation of String'Write, requiring a call to Ada.Streams.Write for each character would be a big performance hit, with no real benefits. Therefore, the number of calls to the Read and Write operations is unspecified, and implementations are free (and in fact advised) to do internal buffering. However, we don't want to allow an implementation to buffer all stream output and do only one call to Write when the program terminates: the user must be able to assume reasonable properties regarding the underlying buffering mechanism. That's why we require that all the calls to Read or Write take place before the top-level call to a stream-oriented attribute completes. In other words, an implementation may combine several consecutive calls to Write into a single one, provided these calls all pertain to a single top-level call to the attribute Write (or Output). !appendix !section 13.13.1 !subject Various issues with the stream-oriented attributes !reference RM95 13.13.1 !reference RM95 13.13.2 !reference AI95-00108 !from Pascal Leroy 97-09-02 !reference 1997-15783.a Pascal Leroy 1997-9-2>> !discussion 1 - RM95 13.13.2(27) states that S'Input "creates an object (with the bounds or discriminants, if any, taken from the stream), initializes it with S'Read, and returns the value of the object. Does the verb "initialize" in this sentence refer to the entire initialization process mentioned in RM95 3.3.1(18) and 7.6(10)? In particular, if S is a controlled subtype, or if it contains controlled components, is the Initialize subprogram called? Is the Finalize subprogram called when the intermediate object is finalized? For a record type whose components have initial values, are these values evaluated? It seems that the simplest model is that S'Input declares a local object, which is then passed to S'Read, as in: X : S (); begin S'Read (..., X); If we don't do that, the object passed to S'Read might have uninitialized access values, or uninitialized compiler-specific fields (dopes, offsets, tag, etc.). 2 - RM95 13.13.2(36) states that "an attribute_reference for one of these attributes is illegal if the type is limited, unless the attribute has been specified by an attribute definition clause." If some stream-oriented attribute has been specified in a private part, and we are at a point that doesn't have visibility over that private part, is a reference to the attribute legal? For example: package P is type T is limited private; private procedure Read (Stream : ...; Item : out T); for T'Read use Read; end P; with P; procedure Q is X : T; begin T'Read (..., X); -- Legal? end Q; One hopes that the call to T'Read is illegal, overwise it would break the privacy of private types. 3 - Let T a limited type with an attribute_definition_clause for attribute Read, and D a type derived from T, and assume that there is no attribute definition clause for D'Read: type T is limited record ... end record; for T'Read use ...; type D is new T; Is a usage of D'Read legal or not? In other words, shall we consider that, for the purpose of checking RM95 13.13.2(36), "an attribute has been specified" for D? If the answer is "yes", how do the inheritance rules stated in AI95-00108 apply in the case where T is tagged, and D has a component of a task type in its record extension part? How is an implementation expected to read that component? 4 - The definition of the profiles of the predefined S'Read, S'Write, S'Input and S'Read given in RM95 13.13.2 uses the notation "T italic" for the type of the Item parameter. AI95-00145 explains that: "The italicized T shown in the definitions of predefined operators means: - T'Base, for scalars - the first subtype, for tagged types - the type without any constraint, in other cases" (Note that the above sentence says "operators", so strictly speaking it doesn't apply to attributes; it seems that the wording of this AI should be broadened.) When one of these attributes is specified by an attribute definition clause, RM95 13.13.2(36) states that "the subtype of the Item parameter shall be the base subtype if scalar, and the first subtype otherwise." There is a problem because these definitions don't coincide in the case of constrained array types. Consider: type T is new String (1 .. 10); It appears that the type of the Item parameter of the predefined stream-oriented attributes is "T without any constraint" (an anonymous unconstrained array type ), while the type of the Item parameter for a user-defined stream-oriented subprogram is T. In particular, does the presence or absence of an attribute definition clause have any bearing on the legality of the calls? For example, consider the legality of the 'others' choice in an array aggregate; it depends on the existence on an applicable index constraint, and therefore on whether the type of the parameter is constrained or not: package P is type T is new String (1 .. 10); procedure Write (...; Item : in T); private for T'Write use Write; end P; with P; procedure Q is begin T'Write (..., (others => 'a')); -- Legal? end; Is the above call legal? If yes, would it become illegal if the attribute_definition_clause were removed? A possible model is to say that an attribute_definition_clause changes the body of the Read attribute, but not its profile. It's as if the predefined Read was just a wrapper calling the user-specified subprogram. 5 - AI95-00108 states (in the !discussion section) that "for untagged derived types, there is no problem for the derived type inheriting the stream attributes." This doesn't seem clear if the derived type includes a known discriminant part. Consider: type Parent (D1, D2 : Integer := 1) is ...; type Child (D : Integer := 2) is new Parent (D1 => D, D2 => D); Clearly Parent'Write writes two discriminant values. It would seem that Child'Write should only write one discriminant value, which contradicts the simple inheritance rule given in the AI. 6 - RM95 13.13.2(9) states that for a record type, the predefined S'Read reads the components in positional aggregate order. However, the RM95 doesn't seem to specify what happens when exceptions are raised by the calls to the Read attribute for the components. Consider for example the following type declarations: type T1 is range ...; type T2 is range ...; type R (D1 : T1 := ...; D2 : T2 := ...) is record ... end record; Say that attributes_definition_clauses have been given for T1'Read and T2'Read, and consider the call: X : R; ... R'Read (..., X); Assume that an exception is raised by T2'Read. Is the discriminant X.D1 modified? That would be unpleasant if there were components depending on this discriminant! Consider a different example, where there are no discriminants: type T1 is ...; type T2 is ...; type R is record C1 : T1; C2 : T2; end record; Assume that an exception is raised by T2'Read. Is the component X.C1 modified? It would seem that we should stick to the notion that discriminants are only modified by an assignment of entire objects. This probably requires an intermediate object in the case of discriminated types. However, it would seem quite expensive to require the creation of such an intermediate object for types that don't have discriminants. Note that if we adopt the idea that an intermediate object is needed (at least in some cases) then we must define if initialization and finalization of this object take place. 7 - Consider a call to T'Read where T is a type with defaulted discriminants. If the discriminants found in the stream have values different from those of the discriminants of the object passed to T'Read for the Item parameter, and that object is constrained, is Constraint_Error raised? Note that if we adopt the model that an intermediate object is needed in this case, then the constraint check performed when assigning the intermediate object to the actual parameter will raise Constraint_Error. 8 - If T is an abstract type, is the function T'Input abstract? One hopes so, otherwise it makes it possible to create objects of an abstract type. 9 - RM95 13.13.1(1) states that "T'Read and T'Write make dispatching calls on the Read and Write procedures of the type Root_Stream_Type." Is the number of those calls specified? For example, for the predefined implementation of String'Write, is an implementation required to call Ada.Streams.Write for each character? or is it allowed to perform internal buffering and to call Ada.Streams.Write only once for the entire string? Obviously the user could tell the difference by writing a pervert implementation of the Write subprogram, but it would seem that performance considerations should have the priority in this case. _____________________________________________________________________ Pascal Leroy +33.1.30.12.09.68 pleroy@rational.com +33.1.30.12.09.66 FAX **************************************************************** From: Tucker Taft Sent: Friday, February 26, 1999 9:47 AM Subject: Re: Updated AIs AI 195 does not address an issue that we have confronted recently relating to stream attributes. It is very difficult to specify stream attributes for a limited private type, if you require them to be specified in the visible part. In general, you may not specify a "representation" attribute until after a type is fully defined. But if you also have to specify stream attributes in the visible part to make them usable, then you have a contradiction. There seem to be two solutions. One is to allow stream attributes to be specified before the type is fully defined. The other is to allow them to be specified in the private part. I suppose another option is to have some kind of "incomplete" stream attribute specification, such as "for Lim_Type'Read use <>;" in the visible part, and then complete the definition in the private part. This is rampant invention, of course, but it solves another problem. Types like Exception_Occurrence are required to have stream attributes, but not have any other primitive operations declared in the visible part. However stream attributes can only be defined in terms of some "normal" subprogram, which must necessarily also be visible at the point of the stream attribute definition. Another contradiction... > The update to AI > 195 is partial: I believe that Tucker owns the part of this AI that has to do > with the number of stream elements written/read for a scalar type. Okay, I will handle that part. -Tuck **************************************************************** From: Pascal Leroy Sent: Monday, March 01, 1999 4:14 AM > There seem to be two solutions. One > is to allow stream attributes to be specified before the > type is fully defined. The other is to allow them to be specified > in the private part. I hate to open private parts. In our compiler, this is a real nightmare (we have to do that for pragma Convention, and that's already a big pain in the neck). So my preference would be to allow the specification of stream attributes before the type is fully defined. Remember that we already have an AI that says that 13.1(10) does not apply to stream attributes. This would just be another oddity with stream-oriented attributes. (In fact I believe the stream-oriented attributes should not be considered a representation item, since they do not affect the physical representation of the type; they should be "something else", with much more relaxed rules; but we are not going to rewrite chapter 13...) > I suppose another option is to have some > kind of "incomplete" stream attribute specification, > such as "for Lim_Type'Read use <>;" in the visible part, > and then complete the definition in the private part. This is > rampant invention, of course, but it solves another problem. This would have been a nice idea in '93, but at this point I think the language is insufficiently broken to invent new syntax. > Types like Exception_Occurrence are required to have stream attributes, > but not have any other primitive operations declared in the > visible part. However stream attributes can only be defined > in terms of some "normal" subprogram, which must necessarily also > be visible at the point of the stream attribute definition. > Another contradiction... If there is a problem with this package, then I'd rather see the ARG modify the specification of Ada.Exceptions to include the additional stuff needed for streaming, rather than adding new syntax for this cornercase. Pascal **************************************************************** From: Tucker Taft Sent: Monday, March 01, 1999 8:18 AM > > There seem to be two solutions. One > > is to allow stream attributes to be specified before the > > type is fully defined. The other is to allow them to be specified > > in the private part. > > I hate to open private parts. In our compiler, this is a real nightmare (we > have to do that for pragma Convention, and that's already a big pain in the > neck). > > So my preference would be to allow the specification of stream attributes > before the type is fully defined. Remember that we already have an AI that > says that 13.1(10) does not apply to stream attributes. This would just be > another oddity with stream-oriented attributes. (In fact I believe the > stream-oriented attributes should not be considered a representation item, > since they do not affect the physical representation of the type; they should > be "something else", with much more relaxed rules; but we are not going to > rewrite chapter 13...) Yes, I agree, since 13.1(10) does not apply to stream attributes, we should view them as a special category of attribute, and allow them to be specified on private types and other not-yet-completely-defined types. > > I suppose another option is to have some > > kind of "incomplete" stream attribute specification, > > such as "for Lim_Type'Read use <>;" in the visible part, > > and then complete the definition in the private part. This is > > rampant invention, of course, but it solves another problem. > > This would have been a nice idea in '93, but at this point I think the > language is insufficiently broken to invent new syntax. > > > Types like Exception_Occurrence are required to have stream attributes, > > but not have any other primitive operations declared in the > > visible part. However stream attributes can only be defined > > in terms of some "normal" subprogram, which must necessarily also > > be visible at the point of the stream attribute definition. > > Another contradiction... > > If there is a problem with this package, then I'd rather see the ARG modify > the specification of Ada.Exceptions to include the additional stuff needed for > streaming, rather than adding new syntax for this cornercase. If we allow specification of stream attributes on private types, that will probably simplify the solution enough to not justify adding any declarations to Ada.Exceptions. **************************************************************** From: Tucker Taft Sent: Thursday, June 01, 2000 9:25 PM ... I would suggest that Output and Input piggy back automatically on Write and Read, even for limited types: If the Read attribute of a type can legally be called, then the Input attribute has a non-abstract default implementation; similarly, if the Write attribute of a type can legally be called, then the Output attribute has a non-abstract default implementation. **************************************************************** From: Randy Brukardt Sent: Friday, June 02, 2000 5:12 PM This seems like a good idea, but it's going well beyond the AI, or even what is currently in AI-00195. I don't want to bog the corrigendum down at the last minute with good ideas... I suggest that we add that to AI-195. Clearly, this wouldn't conflict with the current wording (which defines that these always use the default implementation, but leaves them uncallable for limited types). But I'm not sure it is useful for the implementation burden -- Input is a function after all, and limited functions aren't very useful. **************************************************************** Editor's note, 7/7/2000 Original questions 3 & 5 were deleted, as they have been handled in the corrigendum wording for AI-108. I also added Tucker's suggestion to the AI as question 3, and moved up question 11 to take the space of question 5. **************************************************************** Editor's note, 5/25/2001 The stream size stuff was moved to a new AI (AI-270), as suggested by the ARG at the recent meeting in Leuven. **************************************************************** From: Pascal Leory Sent: Friday, August 3, 2001 3:47 AM I am looking at the rules for streaming of limited types in TC1 (specifically in DR 0040), and it seems to me that they are not quite correct. (We never seem to get streams right, no matter how hard or how often we try.) In 13.13.2(9/1), a section pertaining to Read and Write, we have: "For a limited type extension, if the attribute of any ancestor type of T has been directly specified and the attribute of any ancestor type of the type of any of the extension components which are of a limited type has not been specified, the attribute of T shall be directly specified." In 13.13.2(36/1), a section pertaining to all stream attributes: "An attribute_reference for one of these attributes is illegal if the type is limited, unless the attribute has been specified by an attribute_definition_clause or [(for a type extension)] the attribute has been specified for an ancestor type." I understand that for Read and Write, these rules ensure that the attribute is either (1) not callable or (2) complete in the sense that it deals with all the components, including the limited ones. However, the rules don't seem to work well for Input and Output. Consider the following example: type T1 is tagged limited record ... end record; for T1'Output use ... -- Note: no user-defined T1'Write protected type P is ... end P; type T2 is new T1 with record C : P; end record; T2'Output (...); -- Legal? 13.13.2(36/1) seems to say that the reference to T2'Output is legal because T1'Output was specified. 13.13.2(9/1) is not applicable because the attribute in question is not Read or Write, so apparently the type extension is legal. One is left wondering what T2'Output does at execution. Its default implementation is supposed to write the discriminants and then call T2'Write and P'Write. But in this instance there is no T2'Write and no P'Write, so we have a problem. At any rate, the existence of T1'Output is totally irrelevant, since T2'Output is never defined in terms of T1'Output. It seems that 13.13.2(36/1) needs revision. For Input and Output, a reference should be legal iff Read or Write, resp., have been specified for some ancestor. **************************************************************** From: Randy Brukardt Sent: Thursday, August 16, 2001 4:09 PM Tucker had proposed such a rule only a few days before the Potsdam meeting to finalize the Corrigendum. I made an executive decision to postpone that (lest we introduce still more errors), in part because I was not aware of a technical problem with the proposed rules. (Tuck's proposal was to increase consistency, not to fix a bug.) Anyway, I put this proposal as question 3 in the giant stream AI-195. So I think we can handle the correction there. Conveniently, Pascal is the author of that AI (now that the stream size stuff is out of it, we might even be able to finish it). So I recommend that he add this problem to the AI, then put it on the agenda for the meeting. (How's that for passing the buck? :-) ****************************************************************