!standard B.1 10-02-22 AC95-00193/01 !class confirmation 10-02-22 !status received no action 10-02-22 !status received 10-02-12 !subject 'Input on types that cannot be default initialized !summary !appendix !topic 'Input on types that cannot be default-initialized !reference 13.13.2(27) !from Adam Beneschan 10-02-12 !discussion In Ada 2005, it's pretty easy to define a type that cannot be initialized by default: for example, declare a record Rec with a component that is a null-excluding access type with no initial expression. type Rec is record Comp : not null access Some_Other_Rec; end record; X : Rec; -- will raise Constraint_Error Any object declaration of type Rec must have an initial expression, or else a Constraint_Error will be raised at runtime. (In a sense, you could do this in Ada 95 by declaring a record type with a component whose initial expression calls a function that always raises an exception, but this would be pretty obscure.) It appears that this means Rec'Input cannot be used at all, since the semantics (13.13.2(27)) specify that an object of that type is created and default-initialized, and then passed to 'Read. Is this desirable? **************************************************************** From: Randy Brukardt Date: Friday, February 12, 2010 8:56 PM > Is this desirable? No, it is not desirable to declare a type like this. :-) Seriously, this looks like a language-lawyer question to me. I've tried to write types like this, but it is virtually never the case that an access component will never take on the value null. One example is our implementation of Unbounded_Strings: type Unbounded_String is new Ada.Finalization.Controlled with record Data : not null access String := new String(1..0); end record; This would have been a fine way to write these, except that it doesn't work: the finalization routine has to free the allocated storage, and what can it put in there instead? Pretty much the only choice is "null". So I don't think null exclusions can be used very often outside of parameters and other short-lived views. (They of course are *very* useful for that.) Thus it doesn't matter that much what happens in this particular case for 'Input. Also note that there are a lot of things that you cannot do with such a type. You can't use "<>" in an aggregate, you can't declare a stand-alone object, etc. If you really need such a type, declaring it without an unknown discriminant to prevent uninitialized declaration is just being nasty to whomever uses the type. (I then went on about 'Input not making sense for such types, forgetting that they're not legal in a stand-alone context. And for a partial view with unknown discriminants, the full view might in fact work. I still think this is a bug, but obviously not one we're going to change.) --- Anyway, to directly address your concern, what exactly is the alternative? We can't allow passing uninitialized junk to T'Read (which might be user-defined and might very well depend on some values in the initial record). Because of the history here, that would be the worst kind of incompatibility: code that used to be legal and work becoming erroneous (if accessing an uninitialized composite component). Maybe that isn't very likely, but it's surely legal, and it would be impossible to detect the problem in any sort of automated way. (Well, maybe an ultra-fancy tool could detect it, but it would have to trace through parameter passing and the like to verify that no reading of parts happens in the user-defined overriding. Not terribly likely to get built, I think.) We can't not call T'Read (because it might be user-defined). (If it's *not* user-defined, we'd have some more flexibility. But it would be weird to have rules so that only the predefined T'Input could work in this case. There is too much magic in the stream attributes as it is.) The notion of inventing "default initialization without checks" just for this case is completely unpalatable. (And it too could cause some incompatibilities, although those would be best described as bugs.) So, in the absence of a better idea, we pretty much have to accept this behavior in an unlikely case. **************************************************************** From: Adam Beneschan Date: Friday, February 12, 2010 9:29 PM > Seriously, this looks like a language-lawyer question to me. It came up in user code, more or less. What happened was that the code used a null-excluding access type as the Element_Type of an instance of Ada.Containers.Indefinite_Hashed_Maps. The GNAT implementation of that package defines stream routines for the Map type, and the Map'Read routine ultimately relies on Element_Type'Input. So a program that instantiates this container package, using a null-excluding access type (or something with a null-excluding access type as a component), can't read a Map using the 'Read or 'Input attributes. (However, I don't think this particular program was going to try to do that.) It's occurred to me that null-excluding access types can be a problem for generics in any case. (Probably it's occurred to others too.) If you use of them as the actual for a generic formal T, it's a problem because you don't really know whether the body of the generic tries to declare an object of type T (with no initializer). In this case, that can't happen because the generic formal type is indefinite, unless 'Input is involved. (I note that GNAT's implementation of Ada.Containers.Hashed_Maps will have that problem even though it doesn't use 'Input; in that case, Map'Read will fail when it tries to allocate an uninitialized record that has an Element_Type as a component.) So it seems that there is a potential problem there, but you're probably right that there's no decent solution. **************************************************************** From: Randy Brukardt Date: Friday, February 12, 2010 9:51 PM ... > It came up in user code, more or less. What happened was that the > code used a null-excluding access type as the Element_Type of an > instance of Ada.Containers.Indefinite_Hashed_Maps. The GNAT > implementation of that package defines stream routines for the Map > type, and the Map'Read routine ultimately relies on > Element_Type'Input. So a program that instantiates this container > package, using a null-excluding access type (or something with a > null-excluding access type as a component), can't read a Map using the > 'Read or 'Input attributes. (However, I don't think this particular > program was going to try to do that.) If they actually wanted to do it, creating their own user-defined 'Input would surely fix the problem. (It would have to use an extended return with an initialized object, but that seems possible, especially if they don't need to call the original 'Read. Probably would need a conversion from a non-null excluding subtype, but that is legal assuming that general access types are involved.) So it isn't a complete show-stopper, just somewhat less convenient than it could be. ... > It's occurred to me that null-excluding access types can be a problem > for generics in any case. (Probably it's occurred to others too.) If > you use of them as the actual for a generic formal T, it's a problem > because you don't really know whether the body of the generic tries to > declare an object of type T (with no initializer). In this case, that > can't happen because the generic formal type is indefinite, unless > 'Input is involved. (I note that GNAT's implementation of > Ada.Containers.Hashed_Maps will have that problem even though it > doesn't use 'Input; in that case, Map'Read will fail when it tries to > allocate an uninitialized record that has an Element_Type as a > component.) Right, like I said, using a null-excluding type for anything other than a short-lived view is likely to get you into trouble sooner or later. It would be nice if types like your original record type could be explicitly declared to have unknown discriminants, so that default initialization of them because illegal. (Then you could at least detect the error early - it's silly to have detect something obvious like this at runtime.) **************************************************************** [Editor's note: Streaming of access types using the default implementation of 'Read/'Input is dubious in any case. 13.13.2(35/2) says such values can be abnormal, and thus cannot be depended upon.] ****************************************************************