!standard 3.3.1(11) 10-11-19 AI05-0228-1/03 !standard 3.3.1(12) !standard 3.3.1(21) !standard 3.5(56/2) !standard 3.6(22/2) !standard 6.4.1(13) !class Amendment 10-10-22 !status work item 10-10-22 !status received 10-10-22 !priority Medium !difficulty Easy !subject Default initial values for scalar and array types !summary Two new aspects are added to provide default initial values for scalar types and arrays of scalar types. !problem The new predicate (see AI05-0153-3) and invariant (see AI05-0146-1) aspects provide unreliable results for objects that are not initialized. This can be prevented when a default initial value is provided. It is easy to provide a default initial value for record types, and access types have one defined by the language, but for other kinds of types, it is impossible. This seems bizarre, given that implementations have the capability of doing default initialization (as for record and access types). !proposal Add an aspect Default_Value that can be used on all scalar types, and an additional aspect Default_Component_Value that can be used on all array of scalar types. !wording Add a new bullet after 3.3.1(11): * The implicit initial value for a scalar subtype that has the Default_Value aspect specified is the value of that aspect converted to the nominal subtype (which might raise Constraint_Error - see 4.6, “Type Conversions”); AARM Ramification: This is a dynamic semantics rule, so the visibility of the aspect specification is not relevant -- if the full type for a private type has the Default_Value aspect specified, partial views of the type also have this implicit initial value. Modify 3.3.1(13): * For a (definite) composite subtype, the implicit initial value of each component with a default_expression is obtained by evaluation of this expression and conversion to the component's nominal subtype (which might raise Constraint_Error [— see 4.6, “Type Conversions”]), unless the component is a discriminant of a constrained subtype (the previous case), or is in an excluded variant (see 3.8.1). For each component that does not have a default_expression, {if the composite subtype has the Default_Component_Value aspect specified, the implicit initial value is the value of that aspect converted to the component's nominal subtype; otherwise, } any implicit initial values are those determined by the component's nominal subtype. [Editor's note: We delete the cross-reference because it has been moved into the new bullet; we don't repeat cross-references in paragraphs that will be read together.] Modify 3.3.1(21): There is no implicit initial value for a scalar subtype{ unless the Default_Value aspect has been specified for the type}. In the absence of an explicit initialization{ or the explicit specification of the Default_Value aspect}, a newly created scalar object might have a value that does not belong to its subtype (see 13.9.1 and H.1). Add after 3.5(56/2): Static Semantics For a scalar type, the following language-defined aspect may be specified with an aspect_specification: Default_Value This aspect shall be specified by a static expression. Default_Value shall be specified only on a full_type_declaration. Name Resolution The expected type for the expression specified for the Default_Value aspect is the type defined by the full_type_declaration. Add after 3.6(22/2): Static Semantics For an array type with a scalar component type, the following language-defined aspect may be specified with an aspect_specification: Default_Component_Value This aspect shall be specified by a static expression. Default_Component_Value shall be specified only on a full_type_declaration. Name Resolution The expected type for the expression specified for the Default_Component_Value aspect is the component type of the array type defined by the full_type_declaration. Add a new bullet after 6.4.1(13): [Note that 6.4.1(13) was modified by AI05-0196-1 -- we echo that wording here, not the original wording.] * For a scalar type that has the Default_Value aspect specified, the formal parameter is initialized from the value of the actual, without checking that the value satisfies any constraint; AARM Reason: This preserves the language design principle that all objects of a type with an implicit initial value are initialized. This is important so that a programmer can guarantee that all objects of a scalar type have a valid value with a carefully chosen Default_Value. [Editor's note: We don't need to modify the composite rule, as the changed definition of "implicit initial values" effectively changes this rule to include arrays with a specified Default_Component_Value.] Modify AARM 6.4.1(15.a): This case covers scalar types {that do not have Default_Value specified}, and composite types whose subcomponent's subtypes do not have any implicit initial values. The view conversion for composite types ensures that if the lengths don't match between an actual and a formal array parameter, the Constraint_Error is raised before the call, rather than after. Modify 13.13.2(35/2): In the default implementation of Read and Input for a composite type, for each scalar component that is a discriminant or [whose component_declaration includes a default_expression]{that has an implicit initial value}, a check is made that the value returned by Read for the component belongs to its subtype. Constraint_Error is raised if this check fails. For other scalar components, no check is made. For each component that is of an access type, if the implementation can detect that the value returned by Read for the component is not a value of its subtype, Constraint_Error is raised. If the value is not a value of its subtype and this error is not detected, the component has an abnormal value, and erroneous execution can result (see 13.9.1). In the default implementation of Read for a composite type with defaulted discriminants, if the actual parameter of Read is constrained, a check is made that the discriminants read from the stream are equal to those of the actual parameter. Constraint_Error is raised if this check fails. AARM To Be Honest: An implementation should always be able to detect the error for an access subtype with a null exclusion; the "if the implementation can detect" is intended to cover non-null access values. AARM Ramification: A scalar component can have an implicit initial value if it has a default_expression, if the component's type has the Default_Value aspect specified, or if the component is that of an array type that have the Default_Component_Value aspect specified. !discussion It is important that the default initial value be something that makes sense for the type. Simply initializing everything to zero is just as likely to cover bugs as eliminate them. However, if a type has a value specifically that means "Unknown" or "Undefined", it makes perfect sense to initialize all objects to that value. That would greatly reduce the chances of invalid objects of the type existing. We considered making these subtype aspects rather than type aspects. That would allow giving different default values to different subtypes. However, given the existence of many ways to create anonymous subtypes it would be very hard to guarantee that all values of a type are initialized. Without that guarantee, these aspects lose a lot of their purpose. !examples Providing a default initial value for an enumeration type with an unknown value: type Item_State is (Unknown, Fooey, Blarch, ...) with Default_Value => Unknown; Providing string type that is initialized with blanks: type Blank_String is array (Positive range <>) of Character with Default_Component_Value => ' '; !ACATS test ACATS tests would be needed for this aspect. !appendix [Editor's note: This thread forked off of a discussion of predicates.] From: Robert Dewar Sent: Wednesday, October 13, 2010 11:21 AM > Well, yeah. We get a lot of support calls about uninit vars. > Too bad there's no language feature to prevent those bugs. All language features that "fix" uninitialized variables are evil. Requiring default initialization or providing default implicit initialization are particularly evil in my view. **************************************************************** From: Bob Duff Sent: Wednesday, October 13, 2010 12:33 PM > All language features that "fix" uninitialized variables are evil. I won't touch that with a barge pole. ;-) Anyway, it's irrelevant to my point, which is that uninit vars are a loophole in constraints, so the argument "predicates must be loophole-free, because constraints are" is bogus. >... Requiring default initialization or providing default implicit >initialization are particularly evil in my view. I certainly agree with that. Too bad JDI didn't (I'm talking about access types). **************************************************************** From: Tucker Taft Sent: Wednesday, October 13, 2010 1:01 PM >> ... Requiring default initialization or providing default implicit >> initialization are particularly evil in my view. > > I certainly agree with that. > > Too bad JDI didn't (I'm talking about access types). The default initial value of "null" for an access type seems OK, since you get an exception if you try to use it as though it had a usable non-null value. It is a default initial value of zero that is dangerous, since it is not distinguishable from a "true" initial value of zero. If there were a "null" value for all types, which corresponded roughly to a floating point signaling NaN, that would be fine in my view. You would get notified if you tried to do anything that presumed the object had an "intentional" initial value. **************************************************************** From: Randy Brukardt Sent: Wednesday, October 13, 2010 1:50 PM Right. The annoying thing is that Ada doesn't have a way to set a default initial value for scalar and array types, for those cases where there is an "obviously" bad value. For instance, many of my enumeration types have an value of "unknown" or something like that. It's purpose is exactly to provide a value for those cases where something has gone wrong or nothing has been calculated yet. Typically, it would be used (manually of course) as a default initial value for objects; it would be nice to be able to force all objects to start with that value rather than random garbage. Ada compilers clearly have the mechanism to deal with default initializations (because of access types and record components), so it is annoying that the programmer can't use that when it is appropriate. **************************************************************** From: Robert Dewar Sent: Wednesday, October 13, 2010 5:29 PM TW, on the IM 7040, there was a great instruction intended only for system diagnostics to set parity wrong on a specified word. WATFor fortran used this to initialize all uninitialized variables (under some option flag). And you got a parity error if you tried to load the value. BTW, I don't think it is worth spending too much time on different checking modes. Compilers will likely provide different checking modes anyway, whatever the standard says. **************************************************************** From: Robert Dewar Sent: Wednesday, October 13, 2010 5:27 PM >> It is a default initial value of zero that is dangerous, since it is >> not distinguishable from a "true" initial value of zero. If there >> were a "null" value for all types, which corresponded roughly to a >> floating point signaling NaN, that would be fine in my view. You >> would get notified if you tried to do anything that presumed the >> object had an "intentional" initial value. Big overhead in the array case having to check, and the trouble is that you MUST be able to have 32 tc integers, represented in the obvious way. It would be a menace if Ada insisted on stealing one value. > Ada > compilers clearly have the mechanism to deal with default > initializations (because of access types and record components), so it > is annoying that the programmer can't use that when it is appropriate. Good point, I think I will invent a pragma. In fact the obvious pragma to extend is pragma Initialize_Scalars; and allow the usage pragma Initialize_Scalars (local_type_NAME); 20 minutes work to allow this! **************************************************************** From: Randy Brukardt Sent: Wednesday, October 13, 2010 5:44 PM > >> It is a default initial value of zero that is dangerous, since it > >> is not distinguishable from a "true" initial value of zero. If > >> there were a "null" value for all types, which corresponded roughly > >> to a floating point signaling NaN, that would be fine in my view. > >> You would get notified if you tried to do anything that presumed > >> the object had an "intentional" initial value. > > Big overhead in the array case having to check, and the trouble is > that you MUST be able to have 32 tc integers, represented in the > obvious way. It would be a menace if Ada insisted on stealing one > value. I agree that it doesn't make sense to do it in all cases; it's just odd to not have the capability when it is useful. [That's Tucker's quote, BTW.] > > Ada > > compilers clearly have the mechanism to deal with default > > initializations (because of access types and record components), so > > it is annoying that the programmer can't use that when it is appropriate. > > Good point, I think I will invent a pragma. In fact the obvious pragma > to extend is > > pragma Initialize_Scalars; > > and allow the usage > > pragma Initialize_Scalars (local_type_NAME); > > 20 minutes work to allow this! At one point, someone suggested having an aspect for this purpose. That would look something like: type Item_State is (Unknown, Fooey, Blarch, ...) with Default_Initial_Value => Unknown; This seems like a good use for an aspect, given that it isn't something that ought to be dependent on the view of the type (or subtype, if we wanted to allow that). The (minor) advantage of the aspect over the pragma is that it allows the default value to be specified; that might be helpful if the "Unknown" value is not the first one in the enumeration. **************************************************************** From: Robert Dewar Sent: Wednesday, October 13, 2010 6:00 PM Well we could allow a default value for Initialize_Scalars or some other pragma. IS in GNAT is not always the lowest value, and can in fact be changed at link time or even execution time (the idea is to try different initial values and see if has an observable effect A disadvantage of an aspect if that is the only mechanism is that it is restricted to Ada 2012 mode. We have a lot of customers still stuck at Ada 95 (partly because of the high level of incompatibility between Ada 95 and Ada 2005). **************************************************************** From: Tucker Taft Sent: Wednesday, October 13, 2010 6:10 PM I believe we already discussed something along these lines in the recent ARG meeting. Didn't we invent an aspect spec like "with Default => blah"? Or am I dreaming that? **************************************************************** From: Randy Brukardt Sent: Wednesday, October 13, 2010 6:37 PM I think we invented it, but I don't think anyone took ownership of it and got it added to the agenda. As such, it is now too late (it wasn't included in the scope approved by WG 9), unless someone can figure out a reason why we need to add it for one of the existing AIs. **************************************************************** From: Robert Dewar Sent: Wednesday, October 13, 2010 6:47 PM Sounds good to me, I think I will implement the aspect and the corresponding pragma, so it really doesn't matter too much if it gets into the standard. It's a good idea! **************************************************************** From: Jean-Pierre Rosen Sent: Thursday, October 14, 2010 3:48 AM I was about to suggest an aspect too, but Randy shot first... > A disadvantage of an aspect if that is the only mechanism is that it > is restricted to Ada 2012 mode. We have a lot of customers still stuck > at Ada 95 (partly because of the high level of incompatibility between > Ada 95 and Ada 2005). OTOH, people will move to the new versions if the benefits balance the disagreements. Providing nice features in the most recent modes is a way to push them. **************************************************************** From: Robert Dewar Sent: Thursday, October 14, 2010 6:13 PM Well in fact in GNAT we systematically provide pragmas to match all aspects in any case, the way the internal implementation of aspects work is to translate to the corresponding pragma or attribute definition clause. So for example, we now have pragma predicate and pragma invariant in GNAT. We are more in the business of providing useful features to people than pushing them to use the latest version of Ada. As I say, the biggest barrier is the rather fierce upwards incompatibility of Ada 2005, this has resulted in a barrier for several large users (the issue with return of limited stuff). We have talked about a mode which would provide Ada 95 + all compatible features, but this is quite a bit of work. ****************************************************************