Version 1.9 of ais/ai-00287.txt

Unformatted version of ais/ai-00287.txt version 1.9
Other versions for file ais/ai-00287.txt

!standard 3.3.1 (05)          03-02-19 AI95-00287/04
!standard 3.6.2 (16)
!standard 3.8 (08)
!standard 3.8 (25)
!standard 4.3 (03)
!standard 4.3.1 (04)
!standard 4.3.1 (08)
!standard 4.3.1 (16)
!standard 4.3.1 (17)
!standard 4.3.1 (20)
!standard 4.3.2 (04)
!standard 4.3.3 (03)
!standard 4.3.3 (05)
!standard 4.3.3 (07)
!standard 4.3.3 (24)
!standard 4.8 (05)
!standard 7.5 (01)
!standard 7.5 (02)
!standard 7.5 (08)
!standard 7.5 (09)
!standard 7.5 (10)
!standard 7.5 (11)
!standard 7.5 (12)
!standard 7.5 (13)
!standard 7.5 (14)
!standard 7.5 (15)
!standard 9.1 (21)
!standard 9.4 (23)
!standard 12.4 (08)
!class amendment 02-02-06
!status Amendment 200Y 03-02-19
!status ARG Approved 5-0-3 03-02-08
!status work item 02-10-09
!status received 02-02-06
!priority Medium
!difficulty Medium
!subject Limited Aggregates Allowed
!summary
limited aggregates are allowed, and limited objects may be initialized using aggregates. This implies that it is possible to declare constants (including deferred constants) of a limited type.
A box ("<>") is allowed in place of an expression to for a component value in an aggregate. Box means to use default initialization for that component.
!problem
Ada's limited types allow programmers to express the idea that "copying values of this type does not make sense". This is a very useful capability; after all, the whole point of a compile-time type system is to allow programmers to formally express which operations do and do not make sense for each type.
However, Ada places certain limitations on limited types that have nothing to do with the prevention of copying. The primary example is aggregates: the programmer is forced to choose between the benefits of aggregates (full coverage checking) and the benefits of limited types. These two features ought to be orthogonal, allowing programmers to get the benefits of both.
The full coverage rules (for aggregates and case statements) are often considered to be one of the primary benefits of Ada over many other languages, especially with type extensions, where some components are inherited from elsewhere.
Another important benefit of Ada is the ability to initialize an object when it is created by an object_declaration or allocator. However, this benefit is unavailable for limited types. Moreover, the lack of this capability makes it impossible to declare constant limited objects.
!proposal
The primary goal of this proposal is to allow aggregates to be of a limited type, and to allow such aggregates as the explicit initial value of objects (primarily objects declared by object declarations, and objects created by allocators).
We do not compromise the safety of limited types. In particular:
- Assignment statements are still illegal for limited types.
- The initialization expression for a limited object cannot be the
name of another object, as that would constitute copying out of that object.
- Aggregates are still allowed only for records and arrays
(and extension aggregates for record extensions). For example, there is no such thing as an aggregate of a task type, or of a limited private type.
We also propose to allow a box ("<>") in place of an expression for a component of an aggregate. The box is an explicit request to use default initialization for that component.
The box capability is needed in the case of limited aggregates, because some components might be limited and still not allow aggregates. For example, if a limited record has a task component, that component needs to be specified as a box, which means to create a new task. The box capability is also useful for all flavors of private types, where an aggregate cannot be written and an explicit initializing value may not be available.
We allow the box capability in general (even when the component is nonlimited), because an arbitrary restriction would add complexity, and because such a restriction would cause trouble with the generic contract model and the package-private contract model.
We retain the benefit of full-coverage checking for aggregates, for both limited and nonlimited types. It is possible for some component of the aggregate to be undefined, but only upon explicit request of the programmer (via the box notation). The intent of the full-coverage checking is to prevent some components to be left out by accident, and this intent is retained. (Note that even without this proposal, one could write an expression for a component where the expression is the name of an uninitialized integer variable. The point of full coverage is simply to ensure that the programmer does not forget about some of the components -- including the ones invented tomorrow.)
From the programmer's point of view, initializing a limited object with an aggregate is essentially equivalent to what one would write without this AI: namely, declare an object, and fill in all the components other than the <> ones. For example:
X: T := (A => A_Val, B => V_Val, C => <>);
is essentially equivalent to:
X: T; ... X.A := A_Val; X.B := B_Val; -- Use default for X.C.
except that the comment in the latter case is compile-time-checkable code in the former case. In both cases, if C has a default expression declared in the record type, that is used, and if not, the normal default initialization for the type of C is used. Similarly:
X: T := (A => A_Val, B => V_Val, others => <>);
is essentially equivalent to:
X: T; ... X.A := A_Val; X.B := B_Val; -- Use defaults for the other components.
We say "essentially" equivalent, because there is one run-time difference: If the defaults for A and B have side effects, they happen in the latter case, but not in the former case. But programmers can pretty much ignore that fact, because defaults shouldn't normally have important side effects.
!wording
Delete "nonlimited" from 4.3(3), 4.3.1(8), twice in 4.3.2(4), 4.3.3(7). These are the rules that require aggregates to be nonlimited.
The following rules forbid initialization of the various kinds of limited objects. Delete them. (Note that initialization of components of limited aggregates was forbidden by consequence of the fact that limited aggregates were forbidden, and the fact that a limited component type makes the containing type limited).
3.3.1(5): An initialization expression shall not be given if the object is of a limited type.
3.8(8): A default_expression is not permitted if the component is of a limited type.
4.8(5): If the designated type is limited, the allocator shall be an uninitialized allocator.
12.4(8): The type of a generic formal object of mode in shall be nonlimited.
Replace the above deleted rules with a single Legality Rule just before 7.5(2):
For an assignment operation that initializes a limited object with the value of an expression, the expression shall be a (possibly parenthesized or qualified) aggregate.
AARM annotation:
Ramification: These are the forbidden initializing assignment operations, where X is the name of a limited object:
- Object: T := X; -- initialization expression
- record
Component: T := X; -- default_expression
end record;
- (Component => X)
-- the expression of a component_association of an aggregate
- (X with ...)
-- an expression used as the ancestor_part of an extension_aggregate
- new T'(X) -- initialization of an allocated object
- generic
Formal: T;
package P is ... end;
package Q is new P(Formal => X); -- actual for generic formal object of mode 'in'
If X were replaced by an aggregate in the examples above, they would be legal.
An assignment operation is always part of an assignment_statement or the initialization of an object. Assignment_statements are already forbidden for limited types, so this rule need only mention initializations.
Just after 7.5(8), add:
Implementation Requirements
For an aggregate of a limited type used to initialize an object as allowed above, the implementation shall not create a separate anonymous object for the aggregate. The aggregate shall be constructed directly in the new object.
----------------
The introductory paragraph of 7.5 currently says:
7.5(1): A limited type is (a view of) a type for which the assignment operation is not allowed. A nonlimited type is a (view of a) type for which the assignment operation is allowed.
It doesn't really say anything; it's just introductory verbiage, and is bracketed in the AARM. We can replace it with:
7.5(1): A limited type is (a view of) a type for which copying (such as for an assignment_statement) is not allowed. A nonlimited type is a (view of a) type for which copying is allowed.
(In fact, as pointed out by the "To be honest" in AARM-7.5(1.c), 7.5(1) is actually wrong.)
The NOTES in 7.5(9-15) are now all wrong. The simplest thing would be to delete them.
In the NOTE at 3.6.2(16), delete ", unless the array type is limited."
In the NOTE at 3.8(25), delete ", unless the record type is limited."
The NOTEs at 7.3.1(12), 9.1(21), and 9.4(23) also need minor rewording.
----------------
4.3.1(4) says:
record_component_association ::= [ component_choice_list => ] expression
Modify this to allow a box in place of the expression, but only if named notation is used:
4.3.1(4):
record_component_association ::= [ component_choice_list => ] expression | component_choice_list => <>
4.3.1(16) says:
If a record_component_association has two or more associated components, all of them shall be of the same type.
This should not apply when a box is used, so add "with an expression":
If a record_component_association with an expression has two or more associated components, all of them shall be of the same type.
After 4.3.1(17), add:
A record_component_association for a discriminant without a default_expression shall have an expression rather than a box.
AARM Annotation:
Reason: Otherwise, one could construct records with undefined discriminant values.
After 4.3.1(19), add:
For a record_component_association with an expression, the expression defines the value for the associated component(s). For a record_component_association with a box, if the component_declaration has a default_expression, that default_expression defines the value for the the associated component(s); otherwise, the associated component(s) are initialized by default as for a standalone object of the component subtype (see 3.3.1).
----------------
4.3.3(2-6) says:
Syntax
array_aggregate ::= positional_array_aggregate | named_array_aggregate
positional_array_aggregate ::= (expression, expression {, expression}) | (expression {, expression}, others => expression)
named_array_aggregate ::= (array_component_association {, array_component_association})
array_component_association ::= discrete_choice_list => expression
Change these syntax rules to allow a box in place of an expression, but only in named notation.
Syntax
array_aggregate ::= positional_array_aggregate | named_array_aggregate
positional_array_aggregate ::= (expression, expression {, expression}) | (expression {, expression}, others => expression) | (expression {, expression}, others => <>)
named_array_aggregate ::= (array_component_association {, array_component_association})
array_component_association ::= discrete_choice_list => expression discrete_choice_list => <>
After 4.3.3(23), add:
Each array component expression defines the value for the associated component(s). For an component given by a box, the associated component(s) are initialized by default (see 3.3.1).
!discussion
The basic idea is that there is nothing wrong with constructing a limited object; copying is the evil thing. One should be allowed to create a new object (whether it be a standalone object, or a formal parameter in a call, or whatever), and initialize that object with an aggregate. In implementation terms, the aggregate is built in place in its final destination -- no copying is necessary, or allowed.
Note: AI-318 generalizes this idea to allowing constructor functions in addition to aggregates. AI-318 involves a substantial implementation effort, whereas this AI is almost trivial to implement; that's the reason for the split.
A purist would argue that it seems ugly to allow limited aggregates (this AI), but not allow them to be wrapped in a constructor function (AI-318). However, from a practical point of view, allowing limited aggregates covers most of the cases, because most of the objects in question are heap objects. Therefore, the constructor function can return access-to-limited; the benefit is that that function can use an aggregate to initialize the heap-allocated object. See below for an example (function New_T).
limited constants were never directly disallowed; they were illegal because limited objects could not be initialized, whereas constants must be initialized. Now that limited objects can be initialized, limited constants are allowed. This capability is particularly important for deferred constants.
Implementation of this AI is almost trivial. The compiler front end needs to relax its rules. There are no new run-time concepts: initializing in place is already required for controlled objects by 7.6(17.1/1), and the semantics of a box used in a component association is essentially the same as for a subtype_mark used as the ancestor_part of an extension_aggregate.
limited types offer other advantages in addition to lack of copying: access discriminants, the ability to take 'access of the "current instance", and the ability to extend the type with task or protected components. It seems a shame to require the programmer to choose between these and aggregates.
Note that copying of limited values is still illegal. For example:
T2: Some_Task_Type := T1; -- Illegal!
will still be forbidden by the rule added just before 7.5(2). Similarly, if a limited type has a task component, an aggregate of the outer type cannot use the name of some task object for the task component. A new task must be created, just as if a normal object of the outer type were declared without explicit initialization.
Note that when a component of an aggregate is given by a box, the value is not "assigned" into the component; the component is just default initialized. Thus, no constraint check is done.
The master associated with a limited aggregate is that of the object being initialized, so no anomalies related to tasking or finalization occur.
The box notation is allowed only with named notation, because positional notation might be error prone. For example, in "(X, <>, Y, <>)", there is no type associated with the boxes, so you might not be leaving out the components you thought you were leaving out.
The "others => <>" notation is allowed even when the associated components are not of the same type. The "others => expression" notation, however, still retains the Ada 83 requirement that all the associated components be of the same type. This rule was presumably invented because it would be weird to resolve the expression more than once with different expected types each time. But there is no particular type associated with the box, so "others => <>" is harmless. Furthermore, "others => <>" is actually safer than "others => expression", because the former uses the default values declared in the record itself.
Similarly, "A | B | C => expression" requires A, B, and C to have the same type, whereas "A | B | C => <>" does not.
There is one slight oddity, which I believe was pointed out by Dan Eilers. This AI allows the creation of constants whose value can be modified:
type Self_Ref is limited private;
type Self_Ref_Ptr is access all Self_Ref;
type Self_Ref is limited record Comp: Integer; Self: Self_Ref_Ptr := Self_Ref'Unchecked_Access; end record;
Const: constant Self_Ref := (Comp => 123, others => <>);
Const.Self.all.Comp := 456; -- Modify component of constant object.
We choose not to do anything to prevent this oddity. It's not much odder than the fact that Finalize can modify constant objects. This is one more case where the compiler had better not store the constant object in ROM.
If programmers are using such self-referential types, they can easily control (prevent) this kind of thing by making the type private.
Compatibility:
This change is not quite upward compatible. Consider:
type Lim is limited record Comp: Integer; end record;
type Not_Lim is record Comp: Integer; end record;
procedure P(X: Lim); procedure P(X: Not_Lim);
P((Comp => 123));
The call to P is currently legal, and calls P(Not_Lim). In the new language, this call will be ambiguous.
This seems like a tolerable incompatibility. It is always caught at compile time, and cases where nonlimitedness is used to resolve overloading have got to be vanishingly rare. The above program, though legal, is highly confusing, and I can't imagine anybody wanting to do that. The current rule was a mistake in the first place: even if limited aggregates should be illegal, that should not be a Name Resolution Rule.
Rejected Alternatives:
We considered using a different notation instead of the box. In particular, one could use a subtype_mark, with similar semantics to the box. This idea came from the subtype_mark that is allowed as the ancestor_part in an extension_aggregate. This idea was rejected because:
- It requires extra rules. We have to say that the type of that
subtype_mark is the right type. And we have to ensure that the constraints on that subtype are appropriate.
- Some implementers find that allowing subtype_marks and expressions
in the same context complicates name resolution, in addition to the complication of implementing the rules in the previous bullet.
- The analogy with extension_aggregates is weak. For an
extension_aggregate, the subtype_mark carries information about which ancestor type to use, whereas all we want here is a single bit of information: "use the default".
- Some people found "subtype_mark" to be an odd way to specify "use
the default expression for the component" (as opposed to using the default initialization for the component's type). But we certainly don't want to allow bypassing of the default expression for the component, and we certainly don't want two different notations.
!example
Here is an example of an abstraction where copying makes no sense, so the type T should be limited. In this case, T has a component of type Semaphore, which is protected, so T must be limited.
We have a constructor function New_T, which creates a new object of type T in the heap, and returns a pointer to it.
package P is type T is limited private; type T_Ptr is access T;
function New_T(X: Integer; Y: Boolean) return T_Ptr; ...
private
protected type Semaphore is ...;
type T is limited record Sem: Semaphore; X: Integer; Y: Boolean; end record;
end P;
package body P is
function New_T(X: Integer; Y: Boolean) return T_Ptr is begin return new T'(Sem => <>, X => X, Y => Y); -- This AI makes this legal. end New_T;
...
end P;
The point of this proposal is that if you add a new component to type T, you can't forget to modify the constructor accordingly, because the compiler will remind you.
Without this proposal, New_T would have to look like this:
function New_T(X: Integer; Y: Boolean) return T_Ptr is Result: T_Ptr := new T; begin Result.X := X; Result.Y := Y; return Result; end New_T;
Here, Result.Sem is silently default-initialized, and the other components are initialized using assignment statements. The problem is that if a new component is added to T, New_T now has a bug.
Imagine that T is actually derived from some other type, and a new component is added to that other type, so T silently inherits a new component. That would make the bug even more likely, because the constructor New_T is far away from the original type in the source code, and there may be many types in the hierarchy, making it difficult to track down all the places in the source code that need to be changed.
This proposal solves that problem with a compile-time rule.
Consider also a limited object declared in a package body. Without this proposal, the non-limited components of that object need to be initialized by assignment statements. But in this case, those statements are typically far separated from the declaration of the object itself. For a non-limited object, one would initialize the object at its declaration point, for the usual reasons. This proposal allows the same benefit to apply to the limited object.
!corrigendum 3.3.1(5)
Replace the paragraph:
An object_declaration without the reserved word constant declares a variable object. If it has a subtype_indication or an array_type_definition that defines an indefinite subtype, then there shall be an initialization expression. An initialization expression shall not be given if the object is of a limited type.
by:
An object_declaration without the reserved word constant declares a variable object. If it has a subtype_indication or an array_type_definition that defines an indefinite subtype, then there shall be an initialization expression.
!corrigendum 3.6.2(16)
Replace the paragraph:
48 A component of an array can be named with an indexed_component. A value of an array type can be specified with an array_aggregate, unless the array type is limited. For a one-dimensional array type, a slice of the array can be named; also, string literals are defined if the component type is a character type.
by:
48 A component of an array can be named with an indexed_component. A value of an array type can be specified with an array_aggregate. For a one-dimensional array type, a slice of the array can be named; also, string literals are defined if the component type is a character type.
!corrigendum 3.8(8)
Delete the paragraph:
A default_expression is not permitted if the component is of a limited type.
!corrigendum 3.8(25)
Replace the paragraph:
61 A component of a record can be named with a selected_component. A value of a record can be specified with a record_aggregate, unless the record type is limited.
by:
61 A component of a record can be named with a selected_component. A value of a record can be specified with a record_aggregate.
!corrigendum 4.3(3)
Replace the paragraph:
The expected type for an aggregate shall be a single nonlimited array type, record type, or record extension.
by:
The expected type for an aggregate shall be a single array type, record type, or record extension.
!corrigendum 4.3.1(4)
Replace the paragraph:
record_component_association ::= [ component_choice_list => ] expression
by:
record_component_association ::= | [ component_choice_list => ] expression component_choice_list => <>
!corrigendum 4.3.1(8)
Replace the paragraph:
The expected type for a record_aggregate shall be a single nonlimited record type or record extension.
by:
The expected type for a record_aggregate shall be a single record type or record extension.
!corrigendum 4.3.1(16)
Replace the paragraph:
Each record_component_association shall have at least one associated component, and each needed component shall be associated with exactly one record_component_association. If a record_component_association has two or more associated components, all of them shall be of the same type.
by:
Each record_component_association shall have at least one associated component, and each needed component shall be associated with exactly one record_component_association. If a record_component_association with an expression has two or more associated components, all of them shall be of the same type.
!corrigendum 4.3.1(17)
Insert after the paragraph:
If the components of a variant_part are needed, then the value of a discriminant that governs the variant_part shall be given by a static expression.
the new paragraph:
A record_component_association for a discriminant without a default_expression shall have an expression rather than <>.
!corrigendum 4.3.1(20)
Insert before the paragraph:
The expression of a record_component_association is evaluated (and converted) once for each associated component.
the new paragraph:
For a record_component_association with an expression, the expression defines the value for the associated component(s). For a record_component_association with a <>, if the component_declaration has a default_expression, that default_expression defines the value for the the associated component(s); otherwise, the associated component(s) are initialized by default as for a standalone object of the component subtype (see 3.3.1).
!corrigendum 4.3.2(4)
Replace the paragraph:
The expected type for an extension_aggregate shall be a single nonlimited type that is a record extension. If the ancestor_part is an expression, it is expected to be of any nonlimited tagged type.
by:
The expected type for an extension_aggregate shall be a single type that is a record extension. If the ancestor_part is an expression, it is expected to be of any tagged type.
!corrigendum 4.3.3(3)
Replace the paragraph:
positional_array_aggregate ::= (expression, expression {, expression}) | (expression {, expression}, others => expression)
by:
positional_array_aggregate ::= (expression, expression {, expression}) | (expression {, expression}, others => expression) | (expression {, expression}, others => <>)
!corrigendum 4.3.3(5)
Replace the paragraph:
array_component_association ::= discrete_choice_list => expression
by:
array_component_association ::= | discrete_choice_list => expression | discrete_choice_list => <>
!corrigendum 4.3.3(7)
Replace the paragraph:
The expected type for an array_aggregate (that is not a subaggregate) shall be a single nonlimited array type. The component type of this array type is the expected type for each array component expression of the array_aggregate.
by:
The expected type for an array_aggregate (that is not a subaggregate) shall be a single array type. The component type of this array type is the expected type for each array component expression of the array_aggregate.
!corrigendum 4.3.3(24)
Insert before the paragraph:
The bounds of the index range of an array_aggregate (including a subaggregate) are determined as follows:
the new paragraph:
Each array component expression defines the value for the associated component(s). For an component given by <>, the associated component(s) are initialized by default (see 3.3.1).
!corrigendum 4.8(5)
Replace the paragraph:
If the type of the allocator is an access-to-constant type, the allocator shall be an initialized allocator. If the designated type is limited, the allocator shall be an uninitialized allocator.
by:
If the type of the allocator is an access-to-constant type, the allocator shall be an initialized allocator.
!corrigendum 7.5(1)
Replace the paragraph:
A limited type is (a view of) a type for which the assignment operation is not allowed. A nonlimited type is a (view of a) type for which the assignment operation is allowed.
by:
A limited type is (a view of) a type for which copying (such as for an assignment_statement) is not allowed. A nonlimited type is a (view of a) type for which copying is allowed.
!corrigendum 7.5(2)
Insert before the paragraph:
If a tagged record type has any limited components, then the reserved word limited shall appear in its record_type_definition.
the new paragraph:
For an assignment operation that initializes a limited object with the value of an expression, the expression shall be a (possibly parenthesized or qualified) aggregate.
!corrigendum 7.5(8)
Insert after the paragraph:
There are no predefined equality operators for a limited type.
the new paragraph:
Implementation Requirements
For an aggregate of a limited type used to initialize an object as allowed above, the implementation shall not create a separate anonymous object for the aggregate. The aggregate shall be constructed directly in the new object.
!corrigendum 7.5(9)
Replace the paragraph:
13 The following are consequences of the rules for limited types:
by:
13 While limited types have an assignment operation, other rules of the language insure that it is never actually invoked. The source of such an assignment operation must be an aggregate, and such aggregates must be built directly in the target object.
!corrigendum 7.5(10)
Delete the paragraph:
!corrigendum 7.5(11)
Delete the paragraph:
!corrigendum 7.5(12)
Delete the paragraph:
!corrigendum 7.5(13)
Delete the paragraph:
!corrigendum 7.5(14)
Delete the paragraph:
14 Aggregates are not available for a limited composite type. Concatenation is not available for a limited array type.
!corrigendum 7.5(15)
Delete the paragraph:
15 The rules do not exclude a default_expression for a formal parameter of a limited type; they do not exclude a deferred constant of a limited type if the full declaration of the constant is of a nonlimited type.
!corrigendum 9.1(21)
Replace the paragraph:
4 A task type is a limited type (see 7.5), and hence has neither an assignment operation nor predefined equality operators. If an application needs to store and exchange task identities, it can do so by defining an access type designating the corresponding task objects and by using access values for identification purposes. Assignment is available for such an access type as for any access type. Alternatively, if the implementation supports the Systems Programming Annex, the Identity attribute can be used for task identification (see C.7).
by:
4 A task type is a limited type (see 7.5), and hence has neither assignment nor predefined equality operators. If an application needs to store and exchange task identities, it can do so by defining an access type designating the corresponding task objects and by using access values for identification purposes. Assignment is available for such an access type as for any access type. Alternatively, if the implementation supports the Systems Programming Annex, the Identity attribute can be used for task identification (see C.7).
!corrigendum 9.4(23)
Replace the paragraph:
15 A protected type is a limited type (see 7.5), and hence has neither an assignment operation nor predefined equality operators.
by:
15 A protected type is a limited type (see 7.5), and hence has neither assignment nor predefined equality operators.
!corrigendum 12.4(8)
Delete the paragraph:
The type of a generic formal object of mode in shall be nonlimited.
!ACATS test
ACATS B and C-Tests should be constructed to test these rules.
!appendix

From: Robert Duff
Sent: Wednesday, February 6, 2002,  6:05 PM

                    Limited Types Considered Limited

One of my homework assignments was to propose changes to "fix" limited
types. This e-mail outlines the problems, and proposed solutions.
I haven't written down every detail -- I first want to find out if there
is any interest in going forward with these changes. Some of these
ideas came from Tucker.

I apologize for doing my homework at the last minute. I hope folks will
have a chance to read this before the meeting, and I hope Pascal is
willing to put it on the agenda.

Ada's limited types allow programmers to express the idea that "copying
values of this type does not make sense". This is a very useful
capability; after all, the whole point of a compile-time type system is
to allow programmers to formally express which operations do and do not
make sense for each type.

Unfortunately, Ada places certain limitations on limited types that have
nothing to do with the prevention of copying. The primary example is
aggregates: the programmer is forced to choose between the benefits of
aggregates (full coverage checking) and the benefits of limited types.
Forcing programmers to choose between two features that ought to be
orthogonal is one of the most frustrating aspects of Ada.

I consider the full coverage rules (for aggregates and case statements)
to be one of the primary benefits of Ada over many other languages,
especially with type extensions, where some components are inherited
from elsewhere. I will refrain from further singing the praises of full
coverage; I assume I'm preaching to the choir.

My goals are:

    - Allow aggregates of limited types.

    - Allow constructor functions that return limited types.

    - Allow initialization of limited objects.

    - Allow limited constants.

    - Allow subtype_marks in aggregates more generally.
      (They are currently allowed only for the parent part in an
      extension aggregate.)

The basic idea is that there is nothing wrong with constructing a
limited object; *copying* is the evil thing. One should be allowed to
create a new object (whether it be a standalone object, or a formal
parameter in a call, or whatever), and initialize that object with a
function call or an aggregate. In implementation terms, the result
object of the function call, or the aggregate, is built in place in its
final destination -- no copying is necessary, or allowed.

All of the above goals except constructor functions are fairly trivial
to achieve, both in terms of language design and in terms of
implementation effort. Constructor functions are somewhat more
involved. However, I am against any language design that allows
aggregates where function calls are not allowed; subprogram calls are
perhaps the single most important tool of abstraction ever invented!
(There is at least one other such case in Ada, and I hate it.)

By "constructor function", I mean a function that returns an object
created local to the function, as opposed to an object that already
existed before the function was called.

Ada currently allows functions to return limited types in two cases,
neither of which achieves the goal here:

    If the limited type "becomes nonlimited" (for example, a limited
    private type whose full type is integer), then constructor functions
    are allowed, but the return involves a copy, thus defeating the
    purpose of limited types. Anyway, this feature is not allowed for
    various types, such as tagged types.

    If the limited type does not become nonlimited, then it is returned
    by reference, and the returned object must exist prior to the
    function call; it cannot be created by the function. In essense,
    these functions don't return limited objects at all; they simply
    return a pointer to a preexisting limited object (or perhaps
    a heap object).

We need a new kind of function that constructs a new limited object
inside of itself, and returns that object to be used in the
initialization of some outer object. The run-time model is that the
caller allocates the object, and passes in a pointer to that object.
The function builds its result in that place; thus, no copying is done
on return.

Because the run-time model for calls to these constructor functions is
different from that of existing functions that return a limited type, we
need to indicate this syntactically on the spec of the function. In
particular, change the syntax of functions so that "return" can be
replaced by "out", indicating a constructor function. In addition,
change the syntax of object declarations to allow "out", as in
"X: out T;"; this marks the object as "the result object" of a limited
constructor function. The reason for "out" is that these things behave
much like parameters of mode 'out'.

Examples:

    type T is tagged limited
        record
            X: ...;
            Y: ...;
            Z: ...;
        end record;

    type Ptr is access T'Class;

    Object_1: constant T := (X => ..., Y => ..., Z => ...);

    function F(X: ...; Y: ...) out T;

    function F(X: ...; Y: ...) out T is
        Result: out T := (X => X, Y => Y, Z => ...);
    begin
        ... -- possible modifications of Result.
        return Result;
    end F;

    Object_2: Ptr := new T'(F(X => ..., Y => ...));
        -- Build a limited object in the heap.

Rules:

Change the rules in 4.3 to allow limited aggregates. This basically
means erasing the word "nonlimited" in a few places.

Change the rule in 3.3.1(5) about initializing objects to allow limited
types. But require the expression to be an aggregate or a constructor
function. ("X: T := Y;", where Y is a limited object, remains illegal,
because that would necessarily involve a copy.)  There are various
analogous rules (initialized allocators, subexpressions of aggregates,
&c) that need analogous changes.

Assignment statements remain illegal for limited types, even if the
right-hand side is an aggregate or limited constructor function.

Allowing constants falls out from the other rules.

Allow a component expression in an aggregate to be a subtype_mark. This
means that the component is created as a default-initialized object.
It's essentially the same thing we already allow in an extension
aggregate; we're simply generalizing it to all components of all
aggregates. This is important, in case some part of the type is
private. There is no reason to limit this capability to limited types.

Specify that limited aggregates are built "in place"; there is always a
newly-created object provided by the context. Note that we already have
one case where aggregates are built in place: (nonlimited) controlled
aggregates. Similarly, the result of a limited constructor function is
built in place; the context of the call provides a newly-created object.
(In the case of "X: T := F(...);", where F says "return G(...);", F will
receive the address of X, and simply pass it on to G.)

If the result object of a limited constructor function contains tasks,
the master is the caller.

For a function whose result is declared "out T", T must be a limited
type; such a function is defined to be a "limited constructor function".

Subtype T must be definite. This rule is not semantically necessary.
However, the run-time model calls for the caller to allocate the result
object, and this rule allows the caller to know its size before the
call. Without this rule, a different run-time model would be required
for indefinite subtypes: the called function would have to allocate the
result in the heap, and return a pointer to it. A design principle of
Ada is to avoid language rules that require implicit heap allocation;
hence this rule. (An alternative rule would be that T must be
constrained if composite, thus eliminating defaulted discriminants.)

A limited constructor function must have exactly one return statement.
The expression must be one of the following:

    - An object local to the function (possibly nested in block
      statements), declared with "out".

    - A function call to a limited constructor function.

    - An aggregate.

    - A parenthesized or qualified expression of one of these.

An object declared "out" must be local to a limited constructor
function.

A constraint check is needed on creation of a local "out" object.
We have to do the check early (as opposed to the usual check on the
return statement), because we need to make sure the object fits in the
place where it belongs (at the call site). If the return expression is
an aggregate, that needs a constraint check, as usual. If the return
expression is a function call, then that function will do whatever
checking is necessary.

Is there an issue with dispatching-on-result functions?
I don't think so.


Compatibility:

This change is not upward compatible. Consider:

    type Lim is limited
        record
            Comp: Integer;
        end record;

    type Not_Lim is
        record
            Comp: Integer;
        end record;

    procedure P(X: Lim);
    procedure P(X: Not_Lim);

    P((Comp => 123));

The call to P is currently legal, and calls P(Not_Lim). In the new
language, this call will be ambiguous.

This seems like a tolerable incompatibility. It is always caught at
compile time, and cases where nonlimitedness is used to resolve
overloading have got to be vanishingly rare. The above program, though
legal, is highly confusing, and I can't imagine anybody wanting to do
that. The current rule was a mistake in the first place: even if
limited aggregates *should* be illegal, that should not be a Name
Resolution Rule.


Other advantages:

One advantage of this change is that it makes the usage style of limited
types more uniform with nonlimited types, thus making the language more
accessible to beginners.

How do you construct an object in Ada?  You call a function. Cool -- no
need for the kludginess of C++ constructors. But if it's limited, you
have to fool about with discriminants -- not something that would
naturally occur to a beginner. And discriminants have various annoying
restrictions when used for this purpose.

How do you capture the result of a function call?  You put it in a
constant: "X: constant T := F(...);". But if it's limited, you have to
*rename* it: "X: T renames F(...);". Again, that's not something that
would naturally occur to a beginner -- and the beginner would rightly
look upon it as a "trick" or a "workaround".

Another point is that the current rules force you into the heap,
unnecessarily. You end up passing around pointers to limited objects,
either explicitly or implicitly, which tends to add complexity to one's
programs.

Limited types offer other advantages in addition to lack of copying:
access discriminants, and the ability to take 'Access of the "current
instance". It seems a shame to require the programmer to choose between
these and aggregates.


Alternatives:

It is not strictly necessary to mark the result object with "out"; the
compiler could deduce this information by looking at the return
statement(s). However, marking the object simplifies the compiler -- it
needs to treat this object specially by using the space allocated by the
caller.

It is not necessary to limit the number of return statements to 1.
However, it seems simplest. We need to prevent things like this:

    function F(...) out T is
        Result_1: out T;
        Result_2: out T;
    begin
        Result_1.X := 123;
        Result_2.X := 456;
        if ... then
            return Result_1;
        else
            return Result_2;
        end if;
    end F;

because we can't allocate Result_1 and Result_2 in the same place!
On the other hand, the following could work:

    function F(...) out T is
        Result: out T;
    begin
        if ... then
            return Result;
        else
            return Result;
        end if;
    end F;

suggesting a rule that all return statements must refer to the *same*
object. But this could work, too:

    function F(...) out T is
    begin
        if ... then
            return (...); -- aggregate
        elsif ... then
            return G(...); -- function call
        elsif ... then
            declare
                Result_1: out T;
            begin
                Result_1.X := 123;
                return Result_1; -- local
            end;
        else
            declare
                Result_2: out T;
            begin
                Result_2.X := 456;
                return Result_2; -- different local
            end;
        end if;
    end F;

because only one of the four different result objects exists at any
given time. I'm not sure how much to relax this rule. Perhaps some
rule about declaring only one of these special result objects in a given
region?

****************************************************************

From: Robert Dewar
Sent: Thursday, February 7, 2002,  9:02 AM

I must say that for me, this entire proposal seems to be insufficiently
grounded in real requirements. I am concerned that the ARG is starting to
wander around in the realm of nice-to-have-neat-language-extensions which
are really rather irrelevant to the future success of Ada. I am not opposed
to a few extensions in areas where a really important marketplace need has
been demonstrated, but the burden for new extensions should be extremely
high in my view, and this extension seems to fall far short of meeting
that burden.

****************************************************************

From: Randy Brukardt
Sent: Thursday, February 7, 2002,  2:19 PM

I hate to be agreeing with Robert here :-), but he's right.

There is a problem worth solving here (the inability to have constants of
limited types), but that could adequately be solved simply by the 'in-place'
construction of aggregates (which we already require in similar contexts).
[I'll post a real-world example of the problem in my next message.] The problem
is relatively limited, and thus the solution also has to be limited, or it
isn't worth it. This whole business of constructor functions only will sink any
attempt to fix the real problem, because it is just too big of a change at this
point.

Bob's concerns about the purity of the language would make sense in a new
language design, but we're working with limited resources here, and simple
solutions are preferred over perfect ones.

****************************************************************

From: Randy Brukardt
Sent: Thursday, February 7, 2002,  3:05 PM

Here is an example that came up in Claw where we really wanted constants of a
limited type:

The Windows registry contains a bunch of predefined "keys", along with user
defined keys. Our original design for the key type was something like (these
types were all private, and the constants were deferred, but I've left that out
for clarity):

    type Key_Type is new Ada.Finalization.Limited_Controlled with record
      Handle : Claw.Win32.HKey := Claw.Win32.Null_HKey;
      Predefined_Key : Boolean := False; -- Is this a predefined key?
                                         -- (Only valid if Handle is not null)
      -- other components.
    end record;

    Classes_Root : constant Key_Type := (Ada.Finalization.Limited_Controlled with
       Handle => 16#80000000#, -- Windows magic number
       Predefined_Key => True, ...);
    Current_User : constant Key_Type := (Ada.Finalization.Limited_Controlled with
       Handle => 16#80000001#, -- Windows magic number
       Predefined_Key => True, ...);
    -- And several more like this.

    procedure Open (Key    : in out Key_Type;
                    Parent : in Key_Type;
                    Name   : in String);

    procedure Close (Key   : in out Key_Type);

    procedure Put (Root_Key   : in Key_Type;
                   Subkey     : in String;
                   Value_Name : in String;
                   Item       : in String);

    -- and so on..

Of course, our favorite compiler rejected the constants as illegal.

So, they were turned into functions.

   function Classes_Root return Key_Type;
   function Current_User return Key_Type;

However, these have the problem that they have to be overridden for any
extensions of the type (as they are primitive). We could have put them into a
child/nested package (to make them not primitive), but that would bend the
structure of the design even further and add an extra package for no good
reason. We also could have made them class-wide, but that would be a misleading
specification, as they can never return anything other than Key_Type. So we
left them in the main package.

Aside: we originally wanted to use these as default parameters for some of the
various primitive routines. However, that would illegal by 3.9.2(11) unless
they are primitive functions. This rule exists so that the default makes sense
in inherited primitives. But we really would have preferred that the default
expressions weren't inherited; they only make sense on the base routines. That
is a problem that probably isn't worth solving though.

Of course, now that we had functions, we had to implement them. The first try
was:

   function Classes_Root return Key_Type is
   begin
       return (Ada.Finalization.Limited_Controlled with
           Handle => 16#80000000#, -- Windows magic number
           Predefined_Key => True, ...);
   end Classes_Root;

But our friendly compiler told us that THIS was illegal, because this is
return-by-reference type, and the aggregate doesn't have the required
accessibility.

So we had to add a library package-level constant and return that:

   Standard_Classes_Root : constant Key_Type := (Ada.Finalization.Limited_Controlled with
       Handle => 16#80000000#, -- Windows magic number
       Predefined_Key => True, ...);

   function Classes_Root return Key_Type is
   begin
       return Standard_Classes_Root;
   end Classes_Root;

But of course THAT is illegal (its the original problem all over again), so we
had to turn that into a variable and initialize it component-by-component in
the package body's elaboration code:

   Standard_Classes_Root : Key_Type;

   function Classes_Root return Key_Type is
   begin
       return Standard_Classes_Root;
   end Classes_Root;

begin
   Standard_Classes_Root.Handle := 16#80000000#; -- Windows magic number
   Standard_Classes_Root.Predefined_Key => True;
   ...

Which is essentially how it is today.

This turned into such a mess that we gave up deriving from it altogether, and
created an entirely new higher-level abstraction to provide the most commonly
used operations in an easy to use form. Thus, we ended up losing out on the
benefits of O-O programming here.

I certainly hope that newcomers to Ada don't run into a problem like this,
because it is a classic "stupid language" problem.

Simply having a way to initialize a limited constant with an aggregate would be
sufficient to fix this problem. "Constructor functions" might add
orthogonality, but seem unnecessary to solve the problem of being able to have
constants as part of an abstraction's specification.

****************************************************************

From: Robert Duff
Sent: Friday, February 8, 2002, 10:12 AM

> Simply having a way to initialize a limited constant with an aggregate would
> be sufficient to fix this problem. "Constructor functions" might add
> orthogonality, but seem unnecessary to solve the problem of being able to
> have constants as part of an abstraction's specification.

Surely you don't mean that we would allow limited aggregates only for
initializing stand-alone constants?!  Surely, you could use them to
initialize variables. And if they can be used to initialize variables,
surely initialized allocators should be allowed. And of course,
parameters.

In *my* programs, much of the data is heap-allocated. I want to say:

    X: Some_Ptr := new T'(...);

when T is limited. Allowing only constants would solve about 1% of *my*
problem.

Are you saying that this is illegal:

    P(T'(...));

and I have to instead write:

    Temp: constant T := (...);

    P(Temp);

?!  That sort of arbitrary restriction is what makes people laugh at the
language.

****************************************************************

From: Tucker Taft
Sent: Friday, February 8, 2002,  5:48 PM

Given the relative complexity of the constructor function
concept compared to the other de-limiting ideas, I would propose
we split the AI. The simpler one would allow:

   1) Aggregates of limited objects, with use of a subtype_mark to mean
      default init of a component;
   2) Explicit initialization of limited objects, both in a declaration and
      an allocator, from an aggregate, with the aggregate built "in place" in
      the target.

The more complex one would address functions constructing limited objects
on behalf of the caller.

The aggregate one seems very straightforward. Almost just eliminate
the existing restriction, presuming that compilers have already learned
how to build aggregates "in place" for controlled types.

The function one looks like a lot of work.

****************************************************************

From: Robert I. Eachus
Sent: Monday, October 7, 2002  9:55 AM

It certainly doesn't have my support.  When I see limited types, I think
in terms of objects that have components of a task type.  Not required,
but always possible. When you have a limited view of a type, you don't
know whether it has a component of a task type.  What does it mean to
have an initialization for such an object?  Do you create a new task
object?  If so who is the master?  If not, what happens to the
"wonderful" idea of full coverage for limited types?

In my opinion, since an aggregate or initialization of a record in
general does guarantee that all fields of an object are initialized,
what guarantee are we extending to limited views?  It seems to me that
all that would be accomplished is to add a false feeling of security.
 Oh, and notice that the discriminants of an object of a limited type
will always be initialized, that is not at issue here.

As far as I am concerned, this AI is an attempt to fix something that is
not broken.  The only objects that must be limited are those which  are
or contain tasks.  Most other limited objects are limited either because
the designer of the type wants to do deep copying or some other
non-trivial semantics which would be bypassed by this scheme, or are
generic formal types where the designer of the generic sees no need for
assignment semantics for the actual type.  The first would be broken by
this scheme, and allowing initialization of generic formal limited types
would only lead to mischief:

        generic
            type L is limited private;
        procedure Mischief (LP: in out L);

        procedure Mischief(LP: in out L) is
            Oops: L := LP;
        ...

****************************************************************

From: Tucker Taft
Sent: Monday, October 7, 2002  10:35 AM

     I think you may have missed the ability to "default initialize"
a component by using the subtype name instead of an expression.
For a component that is a task type, protected type, or limited
private type, this is your only alternative.  So full coverage
is still guaranteed, but for these components, you must explicitly
request default initialization ;-).

Note that for tagged types, the other reason for being limited is
to allow for limited extensions.  In general, I think there are plenty
of situations where copying is not meaningful, but initialization-by-
aggregate would be helpful and more maintainable.

****************************************************************

From: Robert Dewar
Sent: Monday, October 7, 2002  10:43 AM

Can we get some idea of where the request for limited aggregates is coming
from. I have certainly never seen any requests from our users in this area
or questions for which this would be the answer.

****************************************************************

From: Robert Eachus
Sent: Monday, October 7, 2002  6:37 PM

Tucker Taft wrote:

>     I think you may have missed the ability to "default initialize"
> a component by using the subtype name instead of an expression.
> For a component that is a task type, protected type, or limited
> private type, this is your only alternative.  So full coverage
> is still guaranteed, but for these components, you must explicitly
> request default initialization ;-).

I didn't overlook this, just didn't see the point of calling something
full coverage when it isn't.  All you can count on for records and
aggregates is that all discriminants have a value, and all access
components have a (potentially null) value.  As for task objects, you
can't count on a value at all times, since the task component may be
accessable before it is elaborated, or it may become terminated.

> Note that for tagged types, the other reason for being limited is
> to allow for limited extensions.  In general, I think there are plenty
> of situations where copying is not meaningful, but initialization-by-
> aggregate would be helpful and more maintainable.

I'll have to respectfully agree to disagree.  In my programming style, I
almost always expect a limited type to contain state, either explicitly
as a protected object or task object, or implicitly through operations
hidden from the view of the user of the type.  Allowing a user to
override the hidden state seems to me to always be a bad thing, and I am
having trouble understanding where you think this new freedom would be
useful.

****************************************************************

From: Robert Duff
Sent: Monday, October 7, 2002  5:30 PM

> It certainly doesn't have my support.  When I see limited types, I think
> in terms of objects that have components of a task type.  Not required,
> but always possible.

There are many reasons to use limited types other than tasks: protected
objects, access discriminants, self-relative pointers, and any other
type where copying does not make sense.  As I said in the AI, "after
all, the whole point of a compile-time type system is to allow
programmers to formally express which operations do and do not make
sense for each type."

>...  When you have a limited view of a type, you don't
> know whether it has a component of a task type.  What does it mean to
> have an initialization for such an object?  Do you create a new task
> object?  If so who is the master?  If not, what happens to the
> "wonderful" idea of full coverage for limited types?

I think you're missing a couple of points: First, to write an aggregate,
it has to be a record (or array).  We're not proposing to allow
aggregates of a task type.  Second, we're not proposing to allow
initialization via anything other than an aggregate.  So, for example,
you can't say "Oops: L := LP;", because LP is not an aggregate.

Perhaps an example would help:

    package P1 is
        type T1 is limited private;
    private
        type T1 is task...
    end P1;

    use P1;

    package P2 is
        type T2 is limited private;
	...
    private
        type T2 is limited
            record
                X: T1;
                Y: Integer;
                Z: Boolean;
            end record;
    end P2;

    package body P2 is

        procedure P is
            T2_Obj: T2 := (T1, 123, False); -- currently illegal
        begin
            ...

The AI proposes that the above should be legal.  As you say, we don't
know (in the body of P2) whether T1 contains tasks.  In this case it
does.  The semantics of the declaration of T2_Obj are to
default-initialize T2_Obj.X, set T2_Obj.Y to 123, and set T2_Obj.Z to
False.

To answer your above questions about tasks: yes, the initialization of
T2_Obj.X creates a task, just as if you had said "X: T1;" as a
standalone variable declaration.  And the master of this task is that of
T2_Obj, namely procedure P.

I think the AI says all this, but perhaps not very clearly.  Maybe I
should add an example like the above.

And note that we're taking full advantage of the full coverage rules,
above.  All three components of the aggregate are *required* -- either
as an expression (giving the value), or as a type_mark (requesting
default initialization).  There's no way to *accidentally* leave out one
of the components.

Note also that since T1 is limited, and not a record, you would not be
allowed to give an expression (such as the name of some object of type
T1) for component X.

> In my opinion, since an aggregate or initialization of a record in
> general does guarantee that all fields of an object are initialized,
> what guarantee are we extending to limited views?

We're extending the same "full coverage" benefits to limited records.
This benefit is currently missing from the language.  We're not taking
away any assurances.

>...  It seems to me that
> all that would be accomplished is to add a false feeling of security.

No, there's no "false feeling of security" -- just as in the existing
language, aggregates must be complete.

>  Oh, and notice that the discriminants of an object of a limited type
> will always be initialized, that is not at issue here.

True.  Discriminants are always initialized, and this AI does not change
that fact.

> As far as I am concerned, this AI is an attempt to fix something that is
> not broken.  The only objects that must be limited are those which  are
> or contain tasks.

As I said above, there are many other uses for limited types in Ada 95,
some of which require limitedness by the language rules, and some of
which require limitedness to correctly define the appropriate behavior
of the type.

>...Most other limited objects are limited either because
> the designer of the type wants to do deep copying or some other
> non-trivial semantics which would be bypassed by this scheme, or are
> generic formal types where the designer of the generic sees no need for
> assignment semantics for the actual type.  The first would be broken by
> this scheme,

No.  Such types are private, not record, and thus would not allow
initialization.

>... and allowing initialization of generic formal limited types
> would only lead to mischief:
>
>         generic
>             type L is limited private;
>         procedure Mischief (LP: in out L);
>
>         procedure Mischief(LP: in out L) is
>             Oops: L := LP;
>         ...

No, this "mischief" would still be illegal -- LP is not an aggregate,
and in fact no aggregate would be legal there, because L is not a
record (as far as the generic knows).

In the above example, if you add another component to T2, you can't
forget to initialize that component of T2_Obj, because the compiler will
tell you.  I had this very bug just last week.  I found it by reading my
own code before I ever executed it, so no real harm was done, but it
would have been better if the compiler could catch it.  The compiler
couldn't, because I was forced to write a series of assignment
statements, one for each component, instead of an aggregate.

I really think the language change proposed by this AI is *very* small.
It doesn't introduce all kinds of tasking issues or anything like that,
nor does it introduce any new run-time concepts for the compiler to deal
with.  It just says you can get the benefit of full coverage rules when
initializing limited records.  Small change, large benefit.

****************************************************************

From: Robert Eachus
Sent: Monday, October 7, 2002  7:17 PM

Robert A Duff wrote:

>Perhaps an example would help:
>
That example helps a lot.  The INTENT is that you can use aggregates to
initialize limited objects that may have limited private components.
 For the limited private, task, or protected object fields, the only
allowed value in the aggregate is the subtype name.

>We're extending the same "full coverage" benefits to limited records.
>This benefit is currently missing from the language.  We're not taking
>away any assurances.
>
>
This is where the devil is in the details, and the whole proposal is
necessary to see the details.  The problem that I am seeing is that
allowing an aggregate as an initial value of a limited object, allows
for an object  to be the value given for a limited component.  To use
Bob Duff's example:

    package P1 is
        type T1 is limited private;
    private
        type T1 is task...
    end P1;

    use P1;

    package P2 is
        type T2 is limited private;
	...
    private
        type T2 is limited
            record
                X: T1;
                Y: Integer;
                Z: Boolean;
            end record;
    end P2;

    package body P2 is

        procedure P is
            T1_Obj: T1; --legal;
            T2_Obj: T2 := (T1_Obj, 123, False); -- Oops!
        begin
            ...

>Note also that since T1 is limited, and not a record, you would not be
>allowed to give an expression (such as the name of some object of type
>T1) for component X.
>
This is the rule that I am unable to deduce from the (draft) AI. Doesn't
mean I think the AI is necessary and useful, but with this rule added it
at least makes sense.  Hmm. Thinking about it, even if the full
declaration of T1 was visible, there is still a potential problem.  The
rule has to be that limited components must be represented in the
aggregate by a subtype mark.  (And thus every object of type T2 in Bob's
example gets a new value of type T1.)  If T1 is visibly not limited in
the body of P2, of course, this AI does not apply, and an aggregate
initialization (or assignment) for objects of T2 is allowed.

****************************************************************

From: Robert Duff
Sent: Monday, October 7, 2002  7:45 PM

> That example helps a lot.

Good.  I should put something like it in the AI.

>...  The INTENT is that you can use aggregates to
> initialize limited objects that may have limited private components.
>  For the limited private, task, or protected object fields, the only
> allowed value in the aggregate is the subtype name.

Right.

> >We're extending the same "full coverage" benefits to limited records.
> >This benefit is currently missing from the language.  We're not taking
> >away any assurances.
> >
> >
> This is where the devil is in the details, and the whole proposal is
> necessary to see the details.  The problem that I am seeing is that
> allowing an aggregate as an initial value of a limited object, allows
> for an object  to be the value given for a limited component.  To use
> Bob Duff's example:
>
>     package P1 is
>         type T1 is limited private;
>     private
>         type T1 is task...
>     end P1;
>
>     use P1;
>
>     package P2 is
>         type T2 is limited private;
> 	...
>     private
>         type T2 is limited
>             record
>                 X: T1;
>                 Y: Integer;
>                 Z: Boolean;
>             end record;
>     end P2;
>
>     package body P2 is
>
>         procedure P is
>             T1_Obj: T1; --legal;
>             T2_Obj: T2 := (T1_Obj, 123, False); -- Oops!

This is forbidden by the following rule in the AI:

| Replace the above deleted rules with a single Legality Rule just before
| 7.5(2):
|
| For an assignment operation that initializes an object with the value of
| a limited expression, the expression shall be one of the following:
|
|     - an aggregate
|     - a qualified_expression whose expression is one of these
|     - a parenthesized_expression whose expression is one of these


T1_Obj is not one of the above (in less formal terms: an aggregate
wrapped in zero or more qual_exps or parens), so it cannot be used to
initialize the component in the aggregate.
The AARM annotation in the AI gives this example as one of the illegal
things:

|     Ramification: These are the forbidden assignment operations,
|     where X is the name of a limited object:
...
|         - (Component => X)
|            -- the expression of a component_association of an aggregate

The point of the rule is that you cannot extra the value out of a
limited object (like T1_Obj), and use it to initialize another object
(like T2_Obj.X) -- that would violate the whole point of limited types,
which is to prevent copying.

In the above example, the only possibility for component X in the
aggregate is a subtype_mark.  It can't be an object name (by the above
rule), and it can't be an aggregate (because T1 is not a record).

Yes, the devil lurks in the details as usual, but I believe I've caught
all of these cases.

>         begin
>             ...
>
> >Note also that since T1 is limited, and not a record, you would not be
> >allowed to give an expression (such as the name of some object of type
> >T1) for component X.
> >
> This is the rule that I am unable to deduce from the (draft) AI.

It's the rule I quoted above.

>... Doesn't
> mean I think the AI is necessary and useful, but with this rule added it
> at least makes sense.  Hmm. Thinking about it, even if the full
> declaration of T1 was visible, there is still a potential problem.  The
> rule has to be that limited components must be represented in the
> aggregate by a subtype mark.

Yes, since T1 is not a record, it must be represented by a subtype_mark
in any aggregate of type T1.

>...(And thus every object of type T2 in Bob's
> example gets a new value of type T1.)

Right -- just as if you declared several standalone variables of type
T1.

>...  If T1 is visibly not limited in
> the body of P2, of course, this AI does not apply, and an aggregate
> initialization (or assignment) for objects of T2 is allowed.

Yes.

****************************************************************

From: Robert Eachus
Sent: Monday, October 7, 2002  10:50 PM

Let me change the example again to show you the one case I am still
"worried about:"

package P1 is
        type T1 is limited private;
         ...
private
       task type T3 is...
       type T1 is limited
          record
              A:  Boolean := False;
              B:  T3;
          end record;
end P1;

package P1.P2 is
    type T2 is limited private;
private
       type T2 is limited
           record
               X: T1;
               Y: Integer;
               Z: Boolean;
          end record;
 end P1.P2;

package body P1.P2  is
       T1_Obj: T1 := (True, T3) -- legal?
       T2_Obj: T2 := ( (True, T3), 123, False); -- legal?
       ...
end P1.P2;

I used child packages to show the methodology issue to its greatest
extent, and yes the legality questions are really questions of
methodology.  I happen to like the idea that it is illegal currently for
a child unit to "forge" an object of type T2, but others may feel
differently.  (Yes, you can muck up an existing object if the full type
is in the parent package body.  I tend to use the old Tucker Taft
ammendment to avoid that.)  However, I definitely feel that the second
case is more egregious than the first.

And this is where the question of style comes to the fore.  My style is
to, where appropriate, hide limitedness by use of  access types.
 Changing the state space of a limited object by adding new state values
is not something to be done on the fly.  You should create a new
"wrapper" type with the new state varibles and leave the original
abstraction untouched.  This allows the base abstraction to be tested
thoroughly, and any wrapper types to be tested as such without worrying
about the internal black box of the base type.

With non-limited types you always have the (potential) worry of user
assignment and user initialization.  I'm not really concerned about the
cases when someone goes outside of Ada by using pragma Interface or
whatever.  Just that most non-limited types can't have dependable state:

     A, B: Foo; -- not a problem case, just for economy of expression....
     C: Foo := A;
   begin
      B := C;
      ...

Yes, if  Foo is a controlled type you can make the state work, if you
are clever.  But I usually find it easier to make Foo a controlled type
with an access value pointing to the real (and limited) state.  Your
milage may vary.

****************************************************************

From: Robert Duff
Sent: Tuesday, October 8, 2002  7:39 AM

>        T1_Obj: T1 := (True, T3) -- legal?
>        T2_Obj: T2 := ( (True, T3), 123, False); -- legal?

Yes, both legal.

These are essentially equivalent to:

        T1_Obj: T1; -- T1_Obj.B gets default initialized.
	T1_Obj.A := True;
        T2_Obj: T2; -- T2_Obj.X.B gets default initialized.
        T2_Obj.X.A := True;
        T2_Obj.Y := 123;
        T2_Obj.Z := False;

except that the version with the aggregates is less error-prone and more
maintainable.

****************************************************************

From: Tucker Taft
Sent: Tuesday, October 8, 2002  4:41 AM

> package body P1.P2  is
>        T1_Obj: T1 := (True, T3) -- legal?

Yes, the new AI would allow this.

>        T2_Obj: T2 := ( (True, T3), 123, False); -- legal?

And this.

>        ...

Without the new AI, you could do essentially the
same thing, but you would not have any full coverage help,
and you would have to separate the declaration from
the initialization, which could create access-before-
full-initialization problems.

E.g.:

    T1_Obj : T1;
    T2_Obj : T2;
 ...
begin
    T1.A := True;
    T2.X.A := True;
    T2.Y := 123;
    T2.Z := False;
 ...
> end P1.P2;
>
> I used child packages to show the methodology issue to its greatest
> extent, and yes the legality questions are really questions of
> methodology.  I happen to like the idea that it is illegal currently for
> a child unit to "forge" an object of type T2, but others may feel
> differently.  (Yes, you can muck up an existing object if the full type
> is in the parent package body.  I tend to use the old Tucker Taft
> ammendment to avoid that.)  However, I definitely feel that the second
> case is more egregious than the first.

I have lost you here, since you can only write an
aggregate if the type is not private at the point
of the aggregate.  If the type is not private, then
what sort of protection can you have?  At least with
the aggregate all the initialization happens very
visibly at the point of declaration.

This whole argument seems a bit murky to me...
Can you show specifically what protection
you are losing?  For me, this is only adding
safety and maintainability, and not removing
anything.

In answer to the question of need, it has almost
reached the level of "conventional wisdom" that
limited types are a pain, and one limited component
"poisons" the whole record.  With Ada 95, there are
more places where you need to make types limited
simply to allow greater extensibility or take
advantage of protected types, with the
net effect that there are more places where this
"poison" is painful.  Having already established
that aggregates used in initializers need to be
built "in place" for controlled types, the model
of aggregates has already shifted enough to easily
accommodate this proposed lessening of the pain
associated with limited components.

****************************************************************

From: Robert Eachus
Sent: Wednesday, October 9, 2002  1:08 AM

Sometimes it takes some back-and-forth on an ARG issue before it is
clear why there are two different points of view.  Here we have it in a
nutshell.  One man's poison is another's plum pudding.  In this case, we
have gotten through the issues of writeup to make it clear what is
intended and what is not.  That is good, great, and wonderful.

Now we can discuss style and methodology.  My style is always to ask
myself, not must this type be limited, but can I make this type limited.
 In security doctrine this is called the principle of least privilege.
 The idea is that there is a least amount of priviledge that a person,
program, etc., needs to do its job.  If you need read access to a some
file, I should give you read access not read write, and so on.

A good example is a queue.  It is in the nature of queues that copying
them (and for that matter, testing for equality) is an invalid
operation.  So queues should be limited objects whether the
implementation includes a task or protected object directly in the queue
or not.

Stacks are different.  There are many cases where saving and restoring
the state of a stack is a meaningful operation, but for other stacks
copying/assignment may be meaningless.  So provide two different
implementations of stacks, one limited, the other not.  The limited
stack may actually be implemented by using the non-limited version, but
it is worth having both to allow the software architect to make a
statement about reality.  This may seem to multiply container entities
beyond reason, but it practice it doesn't.  Tasking safe (protected)
data structures should never be copied--except inside the implementation
to grow them or compact storage.  The stacks you want to be able to copy
are only used by a single thread, and so on.

So I am glad to help make the AI more comprehensible--but I still don't
find it on a list of missing features I want.

****************************************************************

From: Robert A. Eachus
Sent: Wednesday, October 9, 2002  1:20 AM

Robert A Duff wrote:

>Yes, both legal.
>
>
Well, the AI would make both legal.

>These are essentially equivalent to...
>
>...except that the version with the aggregates is less error-prone and more
>maintainable.
>
Sigh.  I guess my point was a bit too subtle.  As far as I am concerned
both have the same bug in that they create an invalid value for a
(nested) object of type T1.  If a type is limited, it is always in my
mind error-prone and less maintainable not to use the actual
constructor(s) and operations for the type.  But as I said in my reply
to Tuck, my only real argument against the AI at this point is that I
see no need for it.  If others see it as very useful in their style of
programming, so be it.  But from my point of view, anything on my wish
list has higher priority. ;-)

****************************************************************

From: Robert Dewar
Sent: Wednesday, October 9, 2002  6:20 AM

<<A good example is a queue.  It is in the nature of queues that copying
them (and for that matter, testing for equality) is an invalid
operation.  So queues should be limited objects whether the
implementation includes a task or protected object directly in the queue
or not.>>

Sorry I do not understand this at all. Why is it in the nature of a queue
that copying them is invalid?

****************************************************************

From: Robert Eachus
Sent: Thursday, October 10, 2002  6:46 PM

A stack may be used by multiple producers and consumers, or it may be
used by a single entity for temporary storage.  If a stack has a single
customer, saving and restoring the state is a meaningful operation.

With a queue, the producer and consumer are almost always separate
entities. In fact, in a well defined queue, the producer and consumer
are decoupled.  That is what the queue does.  So neither the producer or
consumer has enough information to safely save and restore a queue.  On
the other hand it is often appropriate for a producer to close a queue
and for the consumer to close its end of the queue when the queue is
empty and closed by the producer.

A circular buffer with a pair of co-routines is a different animal
altogether.  For example, a parser may have a circular buffer of tokens,
and call the scanner/lexer to create more tokens when they are needed.
 In such a situation, either one could save and restore the circular
buffer, because it is explicitly true that the producer and consumer
cannot be active at the same time.

So I guess it is all a manner of intent, but it maps into semantics.
 For a queue implemented using a circular buffer, the producer and
consumer are unaware of the boundary conditions.  If the queue is full,
a producer wanting to push another object on the queue is blocked.  The
producer doesn't have logiic to deal with the situation, and in fact it
should never "see" it.  The same applies to consumer and an empty queue.

I think I got a bit into this when discussing bounded vs. unbounded,
limited and unlimited, and protected vs. unprotected.  To me for
stacks,  limited, protected, unbounded and unlimited, unprotected,
bounded are the interesting cases, the other combinations are of very
little or no interest.

****************************************************************

From: Robert Dewar
Sent: Wednesday, October 9, 2002  7:18 AM

<<With a queue, the producer and consumer are almost always separate
entities. In fact, in a well defined queue, the producer and consumer
are decoupled.  That is what the queue does.  So neither the producer or
consumer has enough information to safely save and restore a queue.  On
the other hand it is often appropriate for a producer to close a queue
and for the consumer to close its end of the queue when the queue is
empty and closed by the producer.>>

What a narrow view!!!

What about a work pile algorithm, where a single process processes items
off the front of the queue adding elements to the end of the queue. This
to me is an absolutely standard use of queues. In this context, copying
a queue (e.g. for some kind of recursive save/restore) makes absolutely
perfect sense.

Plesae don't impose this narrow producer/consumer view of a queue. This
is Ada myopia induced by excessive tasking :-)

****************************************************************

From: Robert Duff
Sent: Wednesday, October 9, 2002  9:09 AM

>   If a type is limited, it is always in my
> mind error-prone and less maintainable not to use the actual
> constructor(s) and operations for the type.

Yes, but my point is that writing those constructors is much *less*
error-prone if they can use aggregates.

I said in a previous message that I had a bug a week or so ago that
would have been prevented by using aggregates.  (In fact, it was found
by reading the code, but I might have missed it, and it might have been
a run-time bug.)  This bug was *in* a very straightforward constructor.
This particular package and its children have about 50 such
constructors.  These would all be safer and more maintainable if they
could use aggregates.

****************************************************************

From: Robert Dewar
Sent: Wednesday, October 9, 2002  9:22 AM

<<Yes, but my point is that writing those constructors is much *less*
error-prone if they can use aggregates.>>

I strongly agree with this, the use of aggregates systematically to
make sure that the addition of new components gets properly handled
at all constructor points is valuable.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, October 9, 2002  8:55 PM

Robert Eachus wrote:

> Allowing a user to override the hidden state seems to me to always be
> a bad thing, and I am having trouble understanding where you think this
> new freedom would be useful.

This isn't for the user so much as for the constructor of an abstraction.
The big benefit is that it would allow deferred constants of limited types.
We ran into this problem in Claw, and I sent a long explanation of it during
the original discussion of this AI back in February. You must have missed
it, so here it is again:

Here is an example that came up in Claw where we really wanted constants of
a limited type:

(* Editor's note: See the message above from February 7, 2002 *)

****************************************************************

From: Dan Eilers
Sent: Thursday, October 10, 2002  2:11 PM

Randy wrote:

> The big benefit is that it would allow deferred constants of limited types.

But the !summary says nothing about constants, let alone deferred constants.

The AI states:
> Subtype_marks are allowed in place of expressions in
> component_associations of aggregates, with the same meaning
> as when a subtype_mark is used as the ancestor_part in an
> extension_aggregate.

Is this proposed new use of subtype_marks intended to be applicable
for aggregate component_associations of any type (including unlimited
untagged types)?  It refers to "same meaning" as ancestor_parts,
but ancestor_parts must be tagged.

It seems a little strange that subtype_marks are proposed to be used
to mean the subtype's default initial value when used in one kind of
expression context (aggregate component_associations), but not in
other expression contexts.  There might be occasions where one wants
to refer to the default initial value of a subtype other than in an
aggregate.

It also seems a little strange to allow subtype_marks alone in expressions.
I realize there is the precedent of extension aggregates, but I would
prefer some sort of new attribute such as subtype'default_initialization.
Otherwise, it isn't intuitively obvious what is going on.

****************************************************************

From: Robert Duff
Sent: Thursday, October 10, 2002  3:51 PM

> > The big benefit is that it would allow deferred constants of limited types.
>
> But the !summary says nothing about constants, let alone deferred constants.

Perhaps it should, but I didn't mention it because it's not really a new
rule.  The RM never explicitly forbids limited constants -- other rules
conspire to make them impossible, and the AI happens to make them
possible.

> The AI states:
> > Subtype_marks are allowed in place of expressions in
> > component_associations of aggregates, with the same meaning
> > as when a subtype_mark is used as the ancestor_part in an
> > extension_aggregate.
>
> Is this proposed new use of subtype_marks intended to be applicable
> for aggregate component_associations of any type (including unlimited
> untagged types)?

Yes.

>...It refers to "same meaning" as ancestor_parts, but ancestor_parts
> must be tagged.

I meant the "same meaning" in terms of run-time semantics -- it gives
you the default initial values. I did not mean to restrict it to tagged
or limited types. This may not be clear from the summary, but I think
it's clear from the proposed RM wording.

> It seems a little strange that subtype_marks are proposed to be used
> to mean the subtype's default initial value when used in one kind of
> expression context (aggregate component_associations), but not in
> other expression contexts.  There might be occasions where one wants
> to refer to the default initial value of a subtype other than in an
> aggregate.

I'm reluctant to allow it in other contexts.  The point is to allow the
*initialization* of an object (in this case, a component of an
aggregate) using default initialization.  Regular variable declarations
already have this capability -- you say "X: T;", not "X: T := T;".

I don't really see the point in allowing, for example,

    X := T;

as an equivalent for:

    declare
        Temp: T;
    begin
        X := Temp;
    end;

Also, I don't like to weigh down the proposal with extra bells and
whistles -- it's hard enough to get the simplest, easiest to implement
thing past the vendors (who understandably don't want to do useless work).

> It also seems a little strange to allow subtype_marks alone in expressions.
> I realize there is the precedent of extension aggregates, but I would
> prefer some sort of new attribute such as subtype'default_initialization.
> Otherwise, it isn't intuitively obvious what is going on.

I agree -- it's a little strange.  If it weren't for extension
aggregates, I would look for different syntax.  If we were designing the
language from scratch, I might consider "X => default", where default is
a keyword.  But given the language as it is, I think it best to use the
subtype_mark.

****************************************************************

From: Robert Duff
Sent: Monday, October 14, 2002  12:48 PM

I've been trying to do my homework on AI-287, and I have some
questions.

AI-287 says that aggregates are allowed for limited types, and that a
component of an aggregate can be specified by a subtype_mark instead of
an expression.  The subtype_mark thing is necessary in the limited case,
because you can't use an expression for some limited component types
(e.g., if the component of the aggregate is of a task type, or a limited
private type -- you can't give an object name, because it's limited, and
you can't give an aggregate, because it's not a record or array).  For
uniformity and simplicity, we also allow this notation for non-limited
types.  Note that this is not error-prone: the full-coverage rules are
intended to prevent you from *accidentally* leaving some components
undefined.  But if you explicitly write the subtype_mark, you are
explicitly asking for them to be left undefined, which is obviously not
an accident.

At the ARG meeting, we discussed the issue of how (or whether) to ensure
that the constraints on this subtype_mark are appropriate.  We never
quite finished the discussion, but we seemed to be saying that for
scalars, any constraint on the subtype_mark is ignored, and for
composites, there is a run-time check.  (We ignored access subtypes!)
I have been thinking about this, and I now think think that a run-time
check is inappropriate.  I also dislike the "ignore" part.

Run-time checks are only appropriate when they afford some flexibility
-- i.e. when the corresponding compile-time check is too restrictive.
But I can't think of any case where you would *want* a run-time check.

Here's the legality rule I now want to propose:

    For a record_component_association with a subtype_mark that
    denotes a constrained subtype,
    this subtype shall statically match the subtype of the associated
    component(s).

(and a similar rule for components of array aggregates).

In other words, in the aggregate, you have to give either an
unconstrained subtype, or a constrained one that is known at compile
time to have the "right" constraints.

Here are some examples:

    type Task_Type is task ...;

    subtype Task_Count is Integer range 1..10;
    type Task_Array is array (Task_Count range <>) of Task_Type;
    type Constrained_Task_Array is Task_Array(1..4);
    type R(N_Tasks: Task_Count := 0) is limited
        record
            Tasks: Task_Array(1..N_Tasks);
            More_Tasks: Constrained_Task_Array;
            Nat: Natural := 0;
            Int: Integer'Base := 123;
            Dynamic: Integer range 1..Some_Dynamic_Expression := 1;
        end record;

    X: R := (N_Tasks => 5,
             Tasks => Task_Array,
             More_Tasks => Constrained_Task_Array,
             Nat => Natural,
             Int => Integer'Base,
             Dynamic => Integer'Base);

For the above, the task arrays *must* be given as subtype_marks;
you can't give an object name or an aggregate.

For Tasks, we want to be able to give the unconstrained subtype.
It would be annoying to have to say:

    subtype Task_Array_5 is Task_Array(1..5);
    X: R := (...,
             Tasks => Task_Array_5, -- Illegal!
             ...);

and then have a run-time check that Task_Array_5 has the right bounds.
The proposed rule makes the above illegal -- you *have* to give the
unconstrained array, because no constrained array can statically match a
discriminant-dependent array subtype.

(You could also say (..., Tasks => (others => Task_Type), ...),
but that's beside the point.)

For More_Tasks, you have a choice.  You can say
"More_Tasks => Constrained_Task_Array" or
"More_Tasks => Task_Array".  In the former case, we check at compile
time that the bounds are correct; in the latter case, no particular
bounds are implied by "Task_Array".

Note that there is no need for "sliding": you can always just name the
right constrained array subtype with exactly the right bounds, or else
the unconstrained one.  Anyway, I think sliding is a concept that
applies to expressions and names -- not subtype_marks.

Now, as to the scalar components.  You don't *have* to use subtype_marks
for these -- you would normally use expressions.  But we have decided
subtype_marks should be legal (and I think otherwise generic contract
model problems would rear their ugly heads).  Here, I require that you
give an unconstrained subtype (implying no particular range), or the
*right* constrained subtype.  So:

    For Nat, you can say "Nat => Natural" or "Nat => Integer'Base"
    (or even "Nat => Natural'Base").  I think "Nat => Positive" would be
    confusing and error-prone, whether the constraint on Positive is
    ignored, or run-time checked against Natural.  So "Nat => Positive"
    is illegal (at compile-time).

    For Int, you must say "Int => Integer'Base" -- again, giving a
    particular constraint would be confusing.

    For Dynamic, you must say "Dynamic => Integer'Base", because there
    is no subtype_mark that statically matches the anonymous subtype of
    Dynamic.  If we did a run-time check, you could say:

        subtype My_Subtype is Integer range ...;
        ... Dynamic => My_Subtype ...

    but that would be *very* error prone, because you have to take care
    to make sure the dynamic bounds are the same.  If you really want to
    give a named constrained subtype here, you really ought to declare
    it before type R, and use that same name as the subtype of Dynamic,
    and also in the aggregate.

To obey the decision made at the ARG meeting to ignore scalar
constraints, I would have to change "denotes a constrained subtype" to
"denotes a constrained composite or access subtype" in the above rule.
But I think the above discussion shows that there is no benefit to that
added complexity -- there is always a handy subtype name lying around
that you can use (either unconstrained, or with the *right* constraint).
(By the way, I think whatever rule applies to composite has to apply to
access -- bleah.)

Note that "Nat => Natural" means to use the default initialization for
Natural (i.e., leave Nat undefined).  It does *not* mean to use the
default expression for Nat given in the record type.  That could be
confusing -- is it the desired semantics?  If not, what is?

Now consider a record that wraps the above R:

    type R2 is
        record
            R_Comp: R; -- Note that R's discriminants are defaulted.
            ... other components
        end record;

    Y: R2 := (R_Comp => R, ...);

Steve Baird suggested that the following should also be allowed:

    subtype My_R is R(N_Tasks => 7);
    Z: R2 := (R_Comp => My_R, ...); -- Illegal!

My proposed rule above makes this illegal: My_R is constrained, and does
not statically match R.

If this were legal, it would presumably mean that Z.R_Comp.N_Tasks is
initialized to 7 (from My_R), and the rest of Z.R_Comp's components are
initialized by default.  In other words, the same sort of initialization
you would get if you declared a standalone object "Obj: My_R;".
That made sense to me during the meeting, but now it seems weird -- the
run-time semantics says Z.R_Comp is initialized by default,
in other words just like "Obj: R;", which is a contradication.
It also seems confusing to use My_R to *initialize* the discriminant of
R_Comp, rather than to *constrain* it.  Note that defaulted
discriminants is the *only* case where the component can be
unconstrained and you can get some useful information from a constrained
subtype_mark.

Instead, you can say:

    Z: R2 := (R_Comp => (N_Tasks => 7,
                         Tasks => Task_Array,
                         More_Tasks => Constrained_Task_Array,
                         Nat => Natural,
                         Int => Integer'Base,
                         Dynamic => Integer'Base),
              ... -- other components of R2
              );

But I have two problems with that:

    - It doesn't quite do the same thing: it leaves Nat undefined,
      whereas the previous (illegal) version of Z initializes Nat to 0.

    - What if, at the point of declaring R2 and writing the aggregate,
      R is a private view?  Then we can't write the nested aggregate for
      R_Comp.

In summary, I would like guidance from the ARG on the following points:

    - Am I correct that a static rule does not prevent anything useful?

    - Is the semantics of "Nat => Natural", leaving Nat undefined rather
      than using the default 0, what we want?

    - Do we need to support the functionality suggested by Steve Baird
      for defaulted discrims?

****************************************************************

From: Robert Eachus
Sent: Monday, October 14, 2002  2:27 PM

Robert A Duff wrote:

>I've been trying to do my homework on AI-287, and I have some
>questions.
>
>
As always the devil is in the details...

>Run-time checks are only appropriate when they afford some flexibility
>-- i.e. when the corresponding compile-time check is too restrictive.
>But I can't think of any case where you would *want* a run-time check.
>
>
I agree.  The "constants of a limited type" argument has probably
convinced me that this proposal has some merit, but run-time checking
just seems stupid, when there will always be an unconstrained or
statically matching subtype for the cases the ARG is trying to permit.

>Now, as to the scalar components.  You don't *have* to use subtype_marks
>for these -- you would normally use expressions.  But we have decided
>subtype_marks should be legal (and I think otherwise generic contract
>model problems would rear their ugly heads).  Here, I require that you
>give an unconstrained subtype (implying no particular range), or the
>*right* constrained subtype.
>
This is a good point.  We can't avoid the problem described below just
by handwaving.  It is always possible to instantiate a generic formal
limited private type as a discrete type.  So I have to assume that this
proposal will allow something like:

generic
   type Foo is limited private;
package Bar;

package body Bar is
   type Foob is limited record...
      Foobar: Foo;
   end record;
   FB: Foob := (..., Foo);
end Bar;

>Note that "Nat => Natural" means to use the default initialization for
>Natural (i.e., leave Nat undefined).  It does *not* mean to use the
>default expression for Nat given in the record type.  That could be
>confusing -- is it the desired semantics?  If not, what is?
>
Ouch!  There are two possibilities here, first, for an aggregate to
eliminate a default initial value is a bad change in the wrong
direction.  In fact, I think it has to be treated as an impossible
solution.  For example, what happens if you give a subtype mark in the
position of a discriminant in a record aggregate? So I think that the
only legitimate solution is to disallow subtype marks for any record
component with an explicit initial value.  It has to be a legality
check, and notice that it only applies to components of the record type.
 (And to components in subaggregates.)  Using a subtype mark for a
record or record component that has (sub)components with default values
is not a problem.

I also think that methodologically the same rule should hold for array
components in subaggregates:

type Initialized_String (Length: Natural) is record
   Str: String(1..Length) := (others => ' ');
end record;
...
IS: Initialized_String := (10, String);

Is just silly, not actively harmful, but I don't see any particular
reason to allow it.

Note that the case:

IS: Initialized_String := (Natural, "This is some error message or
something.");

Might be nice to allow, but would need some more rule smithing.  I am
not recommending that it be allowed.

****************************************************************

From: Robert Duff
Sent: Monday, October 14, 2002  2:51 PM

> Robert A Duff wrote:
>
> >I've been trying to do my homework on AI-287, and I have some
> >questions.
> >
> >
> As always the devil is in the details...

Indeed.  ;-)

...
> >Now, as to the scalar components.  You don't *have* to use subtype_marks
> >for these -- you would normally use expressions.  But we have decided
> >subtype_marks should be legal (and I think otherwise generic contract
> >model problems would rear their ugly heads).  Here, I require that you
> >give an unconstrained subtype (implying no particular range), or the
> >*right* constrained subtype.
> >
> This is a good point.  We can't avoid the problem described below just
> by handwaving.  It is always possible to instantiate a generic formal
> limited private type as a discrete type.  So I have to assume that this
> proposal will allow something like:
>
> generic
>    type Foo is limited private;
> package Bar;
>
> package body Bar is
>    type Foob is limited record...
>       Foobar: Foo;
>    end record;
>    FB: Foob := (..., Foo);
> end Bar;

Yes, this will be legal.  If Foo turns out to be a task type, a new task
is created.  If Foo turns out to be an access type, then FB.Foobar is
set to null.  If Foo turns out to be Integer, then FB.Foobar is left
undefined, as is usual for integers.

> >Note that "Nat => Natural" means to use the default initialization for
> >Natural (i.e., leave Nat undefined).  It does *not* mean to use the
> >default expression for Nat given in the record type.  That could be
> >confusing -- is it the desired semantics?  If not, what is?
> >
> Ouch!  There are two possibilities here, first, for an aggregate to
> eliminate a default initial value is a bad change in the wrong
> direction.

I agree.  The more I think about it, the more I think that "Nat =>
Natural" should mean that the default expression for Nat is used, if
there is one, and if not, the default for Natural is used (which is to
leave it undefined).

It's *almost* as if you just declared an object of the type (and default
initialized it), and then use assignment statements to initialize those
components that were given as expressions in the aggregate -- that's the
code you have to write without this AI.  The difference is that this
model would initialize some components twice, which is not desired.

>...In fact, I think it has to be treated as an impossible
> solution.  For example, what happens if you give a subtype mark in the
> position of a discriminant in a record aggregate?

That is explicitly disallowed by the AI:

    After 4.3.1(17), add:

    A record_component_association for a discriminant shall have an
    expression rather than a subtype_mark.

    AARM Annotation:

	Reason: Otherwise, one could construct records with undefined
	discriminant values.

>... So I think that the
> only legitimate solution is to disallow subtype marks for any record
> component with an explicit initial value.  It has to be a legality
> check, and notice that it only applies to components of the record type.
>  (And to components in subaggregates.)

Yeah, that's one possibility.  But why not allow it, and say it *uses*
the default declared for that component?

>...Using a subtype mark for a
> record or record component that has (sub)components with default values
> is not a problem.

Right.

> I also think that methodologically the same rule should hold for array
> components in subaggregates:
>
> type Initialized_String (Length: Natural) is record
>    Str: String(1..Length) := (others => ' ');
> end record;
> ...
> IS: Initialized_String := (10, String);
>
> Is just silly, not actively harmful, but I don't see any particular
> reason to allow it.

I would say it should mean that IS.Str is initialized to all blanks.
The alternative is to make it illegal, as you say.
Having it ignore the all-blanks default seems like bad news.

> Note that the case:
>
> IS: Initialized_String := (Natural, "This is some error message or
> something.");
>
> Might be nice to allow, but would need some more rule smithing.  I am
> not recommending that it be allowed.

Agreed.  During the Ada 9X design, we tried to allow something similar
-- namely, leaving out discriminants when they can be inferred from
other components, but it didn't fly.  If that's a good idea, it should
be a separate AI -- I'm not proposing that here.

****************************************************************

From: Tucker Taft
Sent: Monday, October 14, 2002  3:01 PM

I am now concerned that we allow explicitly
creating record objects that have uninitialized
components, when the components have default
expressions.  Until now, we could safely assume
that a component with a default expression could
*never* be uninitialized, and we took advantage of
this in various checks.  Essentially, we always "believe"
the subtype of a component with a default expression,
whereas we don't always "believe" the subtype of a
component without a default expression.

I see three solutions to this problem:
   1) You *must* use an expression rather than a subtype_mark
        if the component has a default expression.  I presume the
        requirement to use an expression rather than a subtype_mark
        would always apply to discriminants.

or
   2) Use the component's default expression, if any, when a
       subtype_mark is used for a component of an (extension
       or record) aggregate

or
   3) Allow a subtype_mark only for components for which an
       expression would be disallowed (limited private [extension],
       task, or protected).  Note that this restriction would be
       inconsistent with extension aggregate ancestor parts.

(1) is a bit odd, since it requires that you provide an explicit value
exactly when there *is* a default defined.  (2) would seem to make more
sense to a user, and would preserve the desirable state (as far as the
compiler, at least) that a component with a default is always initialized
in all record objects. (2) is also closer to the semantics of extension
aggregates, in that components in the ancestor part with defaults end
up with those valeus.  (3) allows the subtype_mark feature to be used
only where it is absolutely needed, but that is inconsistent with the
decision made w.r.t. extension aggregate ancestor parts, and seems
unfriendly.

(2) is the only one that allows subtype_mark to be used everywhere,
and is from the user perspective, essentially equivalent to the components
being uninitialized, except that the default expression will be evaluated
(which might have side-effects or raise an exception).

So I guess I favor (2).

I suppose with (2), we still need to decide whether discriminants
should be allowed to be defaulted.

****************************************************************

From: Robert A. Duff
Sent: Monday, October 14, 2002  3:46 PM

> I am now concerned that we allow explicitly
> creating record objects that have uninitialized
> components, when the components have default
> expressions.  Until now, we could safely assume
> that a component with a default expression could
> *never* be uninitialized, and we took advantage of
> this in various checks.  Essentially, we always "believe"
> the subtype of a component with a default expression,
> whereas we don't always "believe" the subtype of a
> component without a default expression.
>
> I see three solutions to this problem:
>    1) You *must* use an expression rather than a subtype_mark
>         if the component has a default expression.  I presume the
>         requirement to use an expression rather than a subtype_mark
>         would always apply to discriminants.

This is what Robert Eachus suggested.  It works, but I think (2) below
is better.

> or
>    2) Use the component's default expression, if any, when a
>        subtype_mark is used for a component of an (extension
>        or record) aggregate

This is what I (now) like.

> or
>    3) Allow a subtype_mark only for components for which an
>        expression would be disallowed (limited private [extension],
>        task, or protected).  Note that this restriction would be
>        inconsistent with extension aggregate ancestor parts.

I hadn't thought about that possibility.  It works, but I still think
(2) is best.

> (1) is a bit odd, since it requires that you provide an explicit value
> exactly when there *is* a default defined.

From the programmer's point of view, it requires that you write the same
thing twice.  It seems to me that if I already wrote it on the component
decl, I don't want to write it again (error-pronedly (is that a word?))
in an aggregate.

I have written code where there's a component that must point to the
whole record:

    type T is limited
        record
            Self: T_Ptr := T'Unchecked_Access;
            ...

which is only allowed for limited types.  I don't want to write it again
in the aggregate, and I don't want it to use the "null" default for
access types.  So that again favors (2).

>...  (2) would seem to make more
> sense to a user, and would preserve the desirable state (as far as the
> compiler, at least) that a component with a default is always initialized
> in all record objects. (2) is also closer to the semantics of extension
> aggregates, in that components in the ancestor part with defaults end
> up with those valeus.  (3) allows the subtype_mark feature to be used
> only where it is absolutely needed, but that is inconsistent with the
> decision made w.r.t. extension aggregate ancestor parts, and seems
> unfriendly.
>
> (2) is the only one that allows subtype_mark to be used everywhere,
> and is from the user perspective, essentially equivalent to the components
> being uninitialized, except that the default expression will be evaluated
> (which might have side-effects or raise an exception).
>
> So I guess I favor (2).

Me, too.  I already wrote it up that way before I saw this message...

> I suppose with (2), we still need to decide whether discriminants
> should be allowed to be defaulted.

...and in my writeup, I decided that an expression is required for a
discriminant only if it has no default.  (The previous version of the AI
always required expressions for discriminants, as requested by Gary
Dismukes.)  I think it's desirable to allow the discriminants to be
defaulted because it's consistent with other components, and because
sometimes discriminant defaults are just defaults (i.e. when they are
access values) -- as opposed to the weird "I never want to use this
default value, but I want the thing to allow unconstrained variables."

If you don't like that, say so (and please explain why).

- Bob

P.S. I asked three questions in my e-mail.  Robert Eachus answered two
of them, and Tuck one of them (thank you both!), but I'd like to hear
more, especially on number 3:

    1. Am I correct that a static rule does not prevent anything useful?

    2. Is the semantics of "Nat => Natural", leaving Nat undefined rather
       than using the default 0, what we want?  (No.)

    3. Do we need to support the functionality suggested by Steve Baird
       for defaulted discrims?

****************************************************************

From: Robert A. Duff
Sent: Monday, October 14, 2002  4:02 PM

Tucker said:

> > or
> >    3) Allow a subtype_mark only for components for which an
> >        expression would be disallowed (limited private [extension],
> >        task, or protected).  Note that this restriction would be
> >        inconsistent with extension aggregate ancestor parts.

and I replied:

> I hadn't thought about that possibility.  It works, but I still think
> (2) is best.

Also, as Robert Eachus pointed out, rule (3) doesn't really prove
anything, given that the actual for a generic formal limited private
type might just be plain old Integer.  And likewise, the full type for a
package-limited-private type might be Integer.

Just more arguments in favor of (2).

> >...  (2) would seem to make more
> > sense to a user, and would preserve the desirable state (as far as the
> > compiler, at least) that a component with a default is always initialized
> > in all record objects.

Yeah, "at least."  That "desirable state" is something the *programmer*
should be able to count on, never mind the compiler.

****************************************************************

From: Tucker Taft
Sent: Monday, October 14, 2002  9:09 PM

[I thought I answered these, but I can't find my reply...]

Robert A Duff wrote:

>I've been trying to do my homework on AI-287, and I have some
>questions.
>
>...
>

>In summary, I would like guidance from the ARG on the following points:
>
>    - Am I correct that a static rule does not prevent anything useful?
>

I would prefer if you always allowed the first subtype.  Requiring
the use of the base type for scalars seems inconvenient and not too
user friendly.  The first subtype is what is recommended in a couple
of places relating to renames, and it would be nice if the first subtype
was generally considered "OK."



>        - Is the semantics of "Nat => Natural", leaving Nat undefined rather
>      than using the default 0, what we want?
>

As we all seem to agree, leaving Nat undefined is bad news.
As you and I seem to agree, using the component default
seems like a good solution.

>
>    - Do we need to support the functionality suggested by Steve Baird
>      for defaulted discrims?
>

I don't think it is critical, but I think consistency with the
semantics of ancestor subtype marks in extension
aggregates argues for allowing it.  It adds a little
complexity to the wording, but the added consistency
probably simplifies the user's job of remembering the
rules.

****************************************************************

From: Robert A. Duff
Sent: Tuesday, October 15, 2002  9:18 PM

> >In summary, I would like guidance from the ARG on the following points:
> >
> >    - Am I correct that a static rule does not prevent anything useful?
> >
>
> I would prefer if you always allowed the first subtype.  Requiring
> the use of the base type for scalars seems inconvenient and not too
> user friendly.

I think I disagree.  Usually, you're going to use the exact same subtype
name in both places (the component_decl and the aggregate).  This is
usually a named constrained subtype, and you don't need the
unconstrained ('Base) subtype.

> ...The first subtype is what is recommended in a couple
> of places relating to renames, and it would be nice if the first subtype
> was generally considered "OK."

Well, those places are broken, IMHO.  (E.g. the fact that subtypes are
ignored in renaming decls.)  So I don't like to pattern new language
features after those.

But I don't feel strongly about it.  If folks think the first subtype
should always be allowed, I give in.

> >        - Is the semantics of "Nat => Natural", leaving Nat undefined rather
> >      than using the default 0, what we want?
>
> As we all seem to agree, leaving Nat undefined is bad news.
> As you and I seem to agree, using the component default
> seems like a good solution.

Agreed.

> >    - Do we need to support the functionality suggested by Steve Baird
> >      for defaulted discrims?
>
> I don't think it is critical, but I think consistency with the
> semantics of ancestor subtype marks in extension
> aggregates argues for allowing it.  It adds a little
> complexity to the wording, but the added consistency
> probably simplifies the user's job of remembering the
> rules.

(Grumble.)  I suppose you're right...

****************************************************************

From: Pascal Leroy
Sent: Tuesday, October 15, 2002  7:32 AM

>    1) You *must* use an expression rather than a subtype_mark
>         if the component has a default expression.  I presume the
>         requirement to use an expression rather than a subtype_mark
>         would always apply to discriminants.
>
> or
>    2) Use the component's default expression, if any, when a
>        subtype_mark is used for a component of an (extension
>        or record) aggregate

I find (2) quite weird; the notion that the subtype name is used to denote the
default expression looks quite artificial to me.  I would prefer (1) together
with a special notation for denoting the default expression without having to
write it a second time.  For instance:

    X : R := (..., Nat => <>, ...);

But I realize that this is new syntax, so it makes the whole proposal look more
costly.  (Well, it is not entirely new syntax if we adopt AI 317 ;-)

****************************************************************

From: Tucker Taft
Sent: Tuesday, October 15, 2002  8:02 AM

> >    1) You *must* use an expression rather than a subtype_mark
> >         if the component has a default expression.  I presume the
> >         requirement to use an expression rather than a subtype_mark
> >         would always apply to discriminants.
> >
> > or
> >    2) Use the component's default expression, if any, when a
> >        subtype_mark is used for a component of an (extension
> >        or record) aggregate
>
> I find (2) quite weird; the notion that the subtype name is used to denote the
> default expression looks quite artificial to me.

(2) isn't really that weird.  It says "do the usual default
initialization for this component" which means use the
specified component default, if any, or do type-based default
initialization if no component default is specified.

> ...I would prefer (1) together
> with a special notation for denoting the default expression without having to
> write it a second time.  For instance:
>
>     X : R := (..., Nat => <>, ...);
>
> But I realize that this is new syntax, so it makes the whole proposal look more
> costly.  (Well, it is not entirely new syntax if we adopt AI 317 ;-)

This might be killing the proposal with kindness ;-).

If you look at the semantics associated with the extension
aggregate subtype_mark, it supports the general idea
of using component defaults (though I realize it is
represented by the enclosing subtype_mark rather than the
component subtype_mark).

To me, (1) is quite weird, because it forces the programmer
to provide explicit expressions exactly where there are
defaults, which is the *reverse* of all other similar
situations.

An alternative approach to this whole AI is to drop the
use of the subtype_marks completely, and go to allowing
"others => <>" meaning that unspecified components are
initialized according to their defaults.  This loses the
full coverage feature, however.

I suppose once we drop the subtype_mark idea, then
also allowing "<>" for an individual
component as you suggest would also make sense.  If we
do that, then we should probably allow that in AI-317 as
well (which currrenly only allows "others => <>").
This would enshrine "<>" to mean "unspecified" in general.

Personally, I would only allow using "<>" with named
or others notation.  "<>" by itself should be reserved
for meaning that the entire list of parameters/discriminants/
components is unspecified, as it is now in unknown discriminant
parts, formal scalar types (list of enumerals omitted), and
formal packages.  In other words, "<>" by itself means
"others => <>" and may only be used if it is the only
thing in the parens.

With this approach, you would get full coverage checking if you use
named notation without others.

So I guess this means we now have an alternative (4):

  (4) Use "<>" rather than subtype_marks for unspecified components.
      Require named or others notation, unless "<>" is the only
      thing in the aggregate.  "(<>)" would be a fully-default-initialized
      record or array.

Hmmmm....

****************************************************************

From: Pascal Leroy
Sent: Tuesday, October 15, 2002  7:32 AM

> An alternative approach to this whole AI is to drop the
> use of the subtype_marks completely, and go to allowing
> "others => <>" meaning that unspecified components are
> initialized according to their defaults.  This loses the
> full coverage feature, however.

No, I think losing the full coverage would be really bad.  I have been bitten in
the past with record aggregates having things like "others => False", and I
added a new boolean component, but False was not appropriate and bad things
happened.  What you suggest would only make matters worse.  So I would
definitely disallow "others => <>".

****************************************************************

From: Tucker Taft
Sent: Tuesday, October 15, 2002  8:27 AM

It seems odd to take this paternalistic attitude now, given
that case statements and aggregates both already allow
"others =>".  I would agree that a programmer should
be able to get default initialization for some components without
needing "others" (e.g. by adopting your suggestion for <> for
individual components), but to say that it is always bad to use "others"
seems presumptious, and more importantly, inconsistent.

Clearly, in a given context or on a given project, it
might make sense to allow or disallow use of others,
but that goes for the other places where it is permitted as well.

****************************************************************

From: Robert Dewar
Sent: Tuesday, October 15, 2002  8:32 AM

I entirely agree. The language design has already decided that others is
a first class citizen. If you don't like it, look to your coding standards
and other tools to enforce your tastes, not the Ada standard.

****************************************************************

From: Tucker Taft
Sent: Tuesday, October 15, 2002  8:52 AM

Pascal Leroy wrote:

>  I have been bitten in
>the past with record aggregates having things like "others => False", and I
>added a new boolean component, but False was not appropriate and bad things
>happened.  What you suggest would only make matters worse.  So I would
>definitely disallow "others => <>".

Actually, "others => <>" might generally be safer than
"others => false" since the person who adds the component
would determine the default value (true or false) rather than
the person writing the aggregate with others.  I have always
found the "others" feature in record aggregates a bit odd,
in that it only works for components of all the same type.

Something like others => <> could actually be useful, if you
are in a situation where you want "poor man's" extensibility
(i.e. by using a text editor on the record definition) without
affecting client code.

On the other hand, if you *want* all clients to be forced to
deal with the new component, then enforce a project standard
that disallows use of others.  Allowing "others => false" but
disallowing "others => <>" seems weird, given the greater
safety provided by "others => <>" to the type "extender."

****************************************************************

From: Pascal Leroy
Sent: Tuesday, October 15, 2002  9:07 AM

Ok, that's a pretty convincing argument, I withdraw the objection.

****************************************************************

From: Robert A. Duff
Sent: Tuesday, October 15, 2002  9:38 AM

> I find (2) quite weird; the notion that the subtype name is used to denote the
> default expression looks quite artificial to me.  I would prefer (1) together
> with a special notation for denoting the default expression without having to
> write it a second time.  For instance:
>
>     X : R := (..., Nat => <>, ...);

Hmm.  This works, but it seems to add complexity, both in the language
and for the programmer.  I'm against adding unnecessary complexity to
this AI, because I want it to succeed!

Perhaps the following will make you more comfortable with the
"naturalness" of using a subtype_mark to mean "use the
default_expression":  It seems to me that this is how the programmer
should think:

In Ada 95, one might write:

    type Some_Limited_Type is limited
        record
            Tasks: Task_Array;
            X: Integer := 123;
            Y: Boolean := False;
        end record;

    X: Some_Limited_Type; -- X.Tasks is default initialized.
    ...
    X.This := 456;
    -- Leave X.That default-initialized.

In the new Ada with AI-287, one can instead write this:

    X: Some_Limited_Type
       := (Tasks => Task_Array, This => 456, That => Boolean);

which has *exactly* the same run-time semantics (the only difference
being the extra compile-time check).  To me, it makes perfect sense that
the subtype_mark notation means "for this component, give me whatever I
would have gotten if I used the old way of doing it".

Of course, if the default_expressions in the record type have side
effects, and those side effect matter to the output of the program,
then the second is not exactly equivalent to the first.  But that's
got to be rare -- most programmers don't want object declarations to go
around "doing stuff"; they want to calculate values to put in the
components.

So, it seems to me, we want a *single* notation that says "give me the
component's default, if there is one, and the component's subtype's
default otherwise".  This is just about the same as "give me what I
would get if I declared an object of the aggregate's type, and then
filled in some of the components with assignment statements".

I don't much care what that notation is.  I would say it should be
"Nat => default", where default is a reserved word.  But then the
reserved word police would have me jailed (and rightly so).

If you want "<>", I could go along with that if we then eliminate the
subtype_mark notation and *always* use "<>".  ("<>" already means 37
things in Ada -- I suppose one more won't hurt.)

> But I realize that this is new syntax, so it makes the whole proposal
> look more costly.  (Well, it is not entirely new syntax if we adopt AI
> 317 ;-)

Depends what you mean by "new syntax".  The AI already proposes to
modify the syntax rules in the RM, even without the "<>" idea.
But you don't have to change your compiler's parser, because
subtype_mark is indistinguishable from expression (at least for most
parsing techniques ;-)).  If we add "<>", parsers must change, but I
don't think that's a big deal.

****************************************************************

From: Robert A. Duff
Sent: Tuesday, October 15, 2002  9:57 AM

> (2) isn't really that weird.  It says "do the usual default
> initialization for this component" which means use the
> specified component default, if any, or do type-based default
> initialization if no component default is specified.

I agree.

> > ...I would prefer (1) together
> > with a special notation for denoting the default expression without having to
> > write it a second time.  For instance:
> >
> >     X : R := (..., Nat => <>, ...);
> >
> > But I realize that this is new syntax, so it makes the whole proposal look more
> > costly.  (Well, it is not entirely new syntax if we adopt AI 317 ;-)
>
> This might be killing the proposal with kindness ;-).

That's what worries me.  We add more bells and whistles to language
proposals until they're too big to swallow.  (Here, the bell or whistle
is to have two notations for doing essentially the same thing -- "use
the normal default".)

> Personally, I would only allow using "<>" with named
> or others notation.

I agree it has to be that way, but for me, that kills the idea.
Much as I love named notation, it's not *always* appropriate.
And if we use "<>" I want it to entirely replace the subtype_mark
notation.

Note that using some notation other than subtype_mark would simplify the
rules about how the constraints have to match (by eliminating those
rules!).

Note that such a notation would lose the capability of specifying the
discriminants, which Steve Baird suggested at the ARG meeting, and
Tucker is on record as advocating.

> So I guess this means we now have an alternative (4):
>
>   (4) Use "<>" rather than subtype_marks for unspecified components.
>       Require named or others notation, unless "<>" is the only
>       thing in the aggregate.  "(<>)" would be a fully-default-initialized
>       record or array.

I don't much like the (<>) part.

On the other hand, it's no worse that just declaring an object.  That is,

    Some_Procedure((<>));

would be exactly equivalent to:

    X: T;
    Some_Procedure(X);

Hmm.  I think we should stick with the idea we have (subtype_mark), and
make it mean what we want it to mean.

****************************************************************

From: Robert A. Duff
Sent: Tuesday, October 15, 2002  9:58 AM

> No, I think losing the full coverage would be really bad.  I have been
> bitten in the past with record aggregates having things like "others =>
> False", and I added a new boolean component, but False was not
> appropriate and bad things happened.  What you suggest would only make
> matters worse.  So I would definitely disallow "others => <>".

I agree.  The whole point of this AI is to get full coverage for
constructors of limited types, which we already know is a big win in the
nonlimited case.

Of course, records already allow "others =>" notation.  Perhaps that was
a mistake in Ada 83, but we're obviously not going to fix that now.
I think we should leave the rules for others alone -- namely, that in a
record, you can only give others if the components are all the same
type.  And programmers should know that you should rarely use others
(not just in aggregates, but case statements as well).  "Others" really
means "all the others, including the ones that have not yet been
invented".  So you should not use others as a shorthand for "all the
rest that I don't feel like typing in".  You should use others only if
you're very sure that all new cases invented in the future will
necessarily fall into the same category.  That's rare.

For uniformity, I think we should allow "others => subtype_mark" (or
"others => <>") in the cases where others is already allowed.  We should
not extend the rules (as I think Tucker was suggesting) to allow
"others => <>" in a record aggregate to mean *all* the other components,
no matter what their type.

On the other hand, there's no harm in "others" being available if you
obey my stylistic rule above (i.e. hardly ever use it).  It's not like
you can use it by accident.

****************************************************************

From: Randy Brukardt
Sent: Friday, October 25, 2002  10:09 PM

Sorry about coming to this discussion so late; I missed the original one
completely and just noticed it now as I was filing mail...

> If you want "<>", I could go along with that if we then eliminate the
> subtype_mark notation and *always* use "<>".  ("<>" already means 37
> things in Ada -- I suppose one more won't hurt.)

Yes, yes, yes!!!!

> > But I realize that this is new syntax, so it makes the whole proposal
> > look more costly.  (Well, it is not entirely new syntax if we adopt AI
> > 317 ;-)
>
> Depends what you mean by "new syntax".  The AI already proposes to
> modify the syntax rules in the RM, even without the "<>" idea.
> But you don't have to change your compiler's parser, because
> subtype_mark is indistinguishable from expression (at least for most
> parsing techniques ;-)).  If we add "<>", parsers must change, but I
> don't think that's a big deal.

I'd much rather have new syntax rather than subtype_marks here. That's
because syntax means adding a couple of rules to the parser file, and
regenerating. OTOH, the 'subtype_mark' proposal means fiddling around with
name resolution. Other places where subtypes or expressions are allowed are
a real pain, and this one is a much more common place.

Moreover, using <> instead of a subtype_mark eliminates all of Bob's
questions (and the associated RM verbiage). It seems silly to write a page
of RM rules to avoid adding one new syntax rule.

I also am suspicious about the supposed similarity to extension aggregates.
You need the subtype mark there, because it can be any ancestor type - and
you need to specify which one. But, Bob's proposal essentially only allows a
single subtype (or one that happens to statically match) -- it provides no
information whatsoever. So that could be a lot to type and read without
giving any information to the compiler - real type names tend to be a lot
longer than the ones in these examples.

If I was doing this, I would use '<>' simply to replace all of the places
where subtype_marks can be used in Bob's proposal, and no more (with one
possible exception). That means that we don't lose positional notation.
   (10, <>, True) should be legal.

The only 'extension' I'd consider is allowing it to match multiple types in
others. The current rule for others in a record is already more restrictive
than necessary (others => 0) is illegal for a record which has two
components of different integer types. And it does seem appealing to be able
to specify that an entire record, or most of it, is default initialized:

    P (Some_Record'(others => <>));
    Q (Some_Record'(Cnt => 10, others => <>));

I think people would find it weird if this was required to only be used for
components of the same type, because nowhere does the type appear. Moreover,
it actually would make 'others' in a record aggregate useful for something.
But I would not insist on it; I'd rather get the rest of the proposal.

****************************************************************

From: Mike Yoder
Sent: Saturday, October 26, 2002  1:22 AM

>The only 'extension' I'd consider is allowing it to match multiple types in
>others. The current rule for others in a record is already more restrictive
>than necessary (others => 0) is illegal for a record which has two
>components of different integer types.
>
Well, I presume you have some sort of special case check in mind here,
otherwise I'd say the current rule isn't too restrictive. Say I wrote

  (others => f(x) + g(y))

for a record type with 6 components, all of different types, and all
leading to different legal overload resolutions of the expression. I
presume this isn't part of what you want, but even if the expression is
a literal it would have to be a multitype expression with multiple
simultaneous overload resolutions. If you only allow literals, how about

   (others => foo)

where foo represents 6 overloaded enumeration literals? (The same is
possible with character literals.)

I suppose <> is different if there's no need to attach a type to it.

****************************************************************

From: Robert A. Duff
Sent: Sunday, November  3, 2002  9:51 AM

Here is a new version of AI-287, "Limited Aggregates Allowed".
Please review.

The main change is to use the notation "<>" to mean "use the defaults",
instead of a subtype_mark for the same purpose.  This syntax was
suggested by Pascal Leroy, if I remember correctly.

"Use the defaults" means to use the default for a component_decl, if
such a default exists, and to use default initialization for the type of
the component otherwise.

I believe I have taken into account the discussion at the Bedford ARG
meeting, plus all the comments received in the last few weeks.

[Editor's note: This is AI95-00287/02.]

****************************************************************

From: Tucker Taft
Sent: Sunday, November  3, 2002 11:52 AM

I found one case where the "old" notation snuck through
(see below). [Editor's note: this was corrected in the posted draft /02.]

Also, you should at least mention the connection between
this proposal and the wider use of "<>" with formal
packages.

In addition, presumably "(<>)" is not a legal 1-element aggregate,
since "(blah)" is not a legal aggregate (it is a parenthesized
expression).  Alternatively, "(<>)" is interpreted specially
as meaning "(others => <>)", which is really only justified if
the analogy with formal discrete types or formal packages is
drawn.

****************************************************************

From: Robert A. Duff
Sent: Sunday, November  3, 2002  12:25 PM

> I found one case where the "old" notation snuck through
> (see below).

Thank you.

> Also, you should at least mention the connection between
> this proposal and the wider use of "<>" with formal
> packages.

I did:

                            Static Semantics

A record_component_association_list of the form <> is equivalent to
others => <>.

AARM Annotation:

    Reason: AI-317 proposes to allow "others => <>" in a
    formal_package_actual_part, and then goes on to define the existing
    syntax "(<>)" to be a shorthand for "(others => <>)".  For
    consistency, we define the same shorthand notation here.  If we were
    starting the language design over from scratch, I suspect we would
    allow *only* the "others => <>" notation in both cases.

...

An array_aggregate of the form (<>) is equivalent to (others => <>).

> In addition, presumably "(<>)" is not a legal 1-element aggregate,
> since "(blah)" is not a legal aggregate (it is a parenthesized
> expression).  Alternatively, "(<>)" is interpreted specially
> as meaning "(others => <>)", which is really only justified if
> the analogy with formal discrete types or formal packages is
> drawn.

The syntax rules I wrote allow (<>) as a record aggregate or array
aggregate, and it means the same as (others => <>).
Also, (T with <>) is an extension aggregate meaning (T with others => <>).

I thought that's what you were pushing for last time we talked about it,
because that's waht AI-317 wants.  I don't much like it, but I'm not
sure why.  (others => <>) just seems more readable to me that plain
(<>).  What do others folks think?

By the way, I see now that I did it wrong:

Modify this to allow (<>), which is a shorthand for (others => <>):

     record_component_association_list ::=
          record_component_association {, record_component_association}
        | null record
        | (<>)

This should be:

     record_component_association_list ::=
          record_component_association {, record_component_association}
        | null record
        | <>

since the parens come from the outer syntax rules.

[Editor's note: This correction also was made in the posted draft /02.]

****************************************************************

From: Tucker Taft
Sent: Tuesday, November 19, 2002  9:02 AM

An interesting ramification of the proposed
amendment allowing aggregates for limited
type initialization is that it would now
be possible to have limited *constants*.
Those don't exist in Ada 83 or Ada 95 (unless
the full type is non-limited).

This implies that tricks that treat a
limited "in" object as a variable are in
jeopardy of doing even more harm.

Conceivably the trick that uses
'unchecked_access to generate a
self-pointer could be disallowed
for constants.  What this effectively
means is that the programmer could not
use "<>" for the self-pointer component
of the aggregate -- they would have to
provide some explicit value (which would
necessarily not be a self-pointer).

Note that controlled objects are already
a bit "funny" in this sense, since the
Finalize routine is allowed to write on
the object, even if it is a constant.
However, in this case, the object is
at least constant throughout its "normal"
lifetime.

****************************************************************

From: Robert Dewar
Sent: Tuesday, November 19, 2002  6:20 PM

Obviously such low level techniques have to be used carefully, just like
Unchecked_Conversion. After all UC can do unlimited harm also, let's keep
things in perpsective :-)

****************************************************************

From: Randy Brukardt
Sent: Thursday, January 16, 2003  5:15 PM

> It might also help solve the dilemma caused by AI-287 and AI-318
> proposing to allow constants of limited types in the face of the
> Rosen trick allowing writing on objects of limited types.   Perhaps
> the Rosen trick could be disallowed on limited types that didn't
> have this pragma applied, although that would be an incompatibility.

I don't see any dilemma here. We essentially have two options:

-- Limited constants aren't quite constant in the case of the Rosen idiom (I
don't think it is a trick - it's pretty much the intended use of the
feature). That means that compilers need to be careful of how they're
allocated, but that's pretty much required anyway as these are always
by-reference types.

That is compatible with the current language, which already has holes
allowing constant views to be written (both for in parameters, and for
access-to-constant types, where the same approach would apply). Those holes
weren't plugged, I suppose, because there weren't any "real" constants --
these objects are all variables when they are declared. Which leads to the
second option:

-- Disallow constants of types using the Rosen idiom or any of its variants.
That essentially means types with a self-referencing access attributes.
However, that can't be done at compile-time, because that would be a
(severe) privacy violation.

A self-reference access in a limited constant is essentially an
access-to-constant. So there could be a run-time check that it isn't used as
an access-to-variable (which includes the access discriminant of the Rosen
idiom). Then any attempt to declare a constant of such a type would raise
Constraint_Error.

Having it be a run-time check is nice in the sense that it provides a way to
have such self-referencing types and still have constants:

  package P is
    type Interesting (<>) is limited private;
    Null_Interesting : constant Interesting;
    -- Operations on Interesting.
  private
    type I_Access is access all Interesting;
    type Interesting (Is_Null : Boolean) is limited record
        case Is_Null is
           when True  => null;
           when False => Self : I_Access := Interesting'Unchecked_Access;
        end case;
    end record;
    Null_Interesting : constant Interesting := (Is_Null => True);
  end P;

On the other hand:
    Very_Interesting : constant Interesting := (Is_Null => False, Self =>
<>);
would raise Constraint_Error, as Self would be putting an access-to-constant
into an access-to-variable (and we can't allow that). The check would have
to apply to all subcomponents of the type (there can't be a workaround).

Since access discriminants are defined to be access-to-variable types, the
check would also apply to them.

This doesn't seem too bad. But I haven't worked out all of the details.
Hopefully Bob Duff (the author of this AI) is listening, because it is clear
that this issue needs to be discussed in the AI.

****************************************************************

From: Dan Eilers
Sent: Friday, January 17, 2003  10:42 AM

Randy wrote:  (option 1)
> -- Limited constants aren't quite constant in the case of the Rosen idiom (I
> don't think it is a trick - it's pretty much the intended use of the
> feature). That means that compilers need to be careful of how they're
> allocated, but that's pretty much required anyway as these are always
> by-reference types.

By "careful of how they're allocated" do you mean that compilers
shouldn't allocate the proposed limited-type constants in read-only memory?

If so, then what exactly is the purpose such constants?  Is it just to get
writeable deferred constants?  If so, it would probably make more sense
to allow variables with deferred initialization.

****************************************************************

From: Randy Brukardt
Sent: Friday, January 17, 2003  1:26 PM

Right. Or fail to actualize them altogether (Janus/Ada doesn't declare an
object for certain small constants at all, creating them only when they're
used.) But you have to actualize by-reference objects, so I don't think that
is the problem.

> If so, then what exactly is the purpose such constants?  Is it just to get
> writeable deferred constants?  If so, it would probably make more sense
> to allow variables with deferred initialization.

IMHO, there is no purpose to such constants. We just can't ban them for the
reasons mentioned in the first note, and the runtime check I outlined there
doesn't seem implementable if a function is used to initialize them.

The purpose of limited constants are for the 99% of limited types that don't
use the Rosen idiom. Those are really constant. I know that I've never used
an access of the enclosing type in all of my years of programming -- because
Janus/Ada blew up with an internal error on any attempt to do it until
recently. One could probably see the same issues in a task constant. If the
task object is in fact a copy of the TCB (that's not how Janus/Ada works),
then it could change while the task runs. But that's not important to the
abstraction: what is important is that the task identified never changes.
(In that sense, all task objects are constants.) It's a much too
implementation-oriented view to say that some of the bits might change, so
its not a constant. The important thing is that it always represent the same
value.

****************************************************************

From: Dan Eilers
Sent: Friday, January 17, 2003  3:33 PM

If I understand you correctly, you are proposing that limited constants
would be allowed, even those that use the Rosen idiom, but the compiler
could allocate a limited constant in ROM if and only if it could detect
that the type didn't use the Rosen idiom.

Would such Rom-able constants be considered pre-elaborable?

****************************************************************

From: Robert A. Duff
Sent: Monday, February  3, 2003  3:39 PM

Here is a new version of AI-287, "Limited Aggregates Allowed".
[Editor's not, this was version /03.]

As usual, I apologize for doing my homework so late.
I hope Pascal can put this on the agenda for the Padua meeting.
I really hope that this AI makes it into the upcoming language
revision.  It might be nice to get AI-318 in as well, but I fear AI-318
involves too much implementation burden, so I want to see this AI
considered separately from AI-318.  I'm not even sure I will bother
revising AI-318 before the Padua meeting.

This is nearly identical to the previous version, which I sent out on
November 3.  The differences are:

    - Corrected the syntax of record_component_association_list.

    - Corrected the example New_T function to use the <> syntax.

    - Added some words to the discussion about the interaction
      with the "Rosen trick", as Dan Eilers called it.

    - Added some discussion about why we should tolerate passing this AI
      without passing AI-318.

    - Fix some typos.

****************************************************************

From: Randy Brukardt
Sent: February 19, 2003

I've made some minor wording changes from those in the approved AI in
order to improve the presentation:
-- I've replaced "box" with <> in the text. This eliminates the need for
   a naive reader to look in the index to find 3.6(15) to find out that
   <> is called box. There doesn't seem to be any advantage to using the
   term here rather than the syntax itself.

-- I've added the following note to 7.5:
   "While limited types have an assignment operation, other rules of the
   language insure that it is never actually invoked. The source of such
   an assignment operation must be an aggregate, and such aggregates must be
   built directly in the target object."

   This is an attempt to explain the schizoid discussion of the assignment
   operation to the end users reading the standard. We really do not want
   anyone to think that assignment=>copying=>copying of limited types. We
   can't reinforce that enough (see the e-mail exchanges in the !appendix).

-- I had problems with Bob's unhelpful "The notes of 7.3.1(12), 9.1(21), and
   9.4(23) need minor rewording." When Bob writes something like that, he
   really means "I think this should be changed, but I don't have the slightest
   idea of what to write." It's even more confusing, because its not at all
   clear what he thought needed to be changed.

   Here are the notes in question:

   9  Partial views provide assignment (unless the view is limited), membership
   tests, selected components for the selection of discriminants and inherited
   components, qualification, and explicit conversion.

   4  A task type is a limited type (see 7.5), and hence has neither an
   assignment operation nor predefined equality operators. If an application
   needs to store and exchange task identities, it can do so by defining an
   access type designating the corresponding task objects and by using access
   values for identification purposes. Assignment is available for such an
   access type as for any access type. Alternatively, if the implementation
   supports the Systems Programming Annex, the Identity attribute can be used
   for task identification (see C.7). Examples

   15  A protected type is a limited type (see 7.5), and hence has neither an
   assignment operation nor predefined equality operators.

   I presume that Bob was concerned that each of these notes says that the
   type in question doesn't have an assignment.

   The first note, which just says 'assignment', is vague enough to leave alone,
   as limited types have assignment in name only. Trying to be more precise
   here is more likely to confuse the reader than help. One of the reasons
   for adding the note in 7.5 is to allow us to say that limited types don't
   have assignment in non-normative text, which is far more likely to be
   helpful to the reader than an pedantic explanation would be.

   The second two notes say that the types don't have an 'assignment
   operation'. I've changed these to the intentionally vaguer 'assignment'.
   I can't think of any wording other wording which is precise enough, is
   simple enough to be a useful note, and still conveys the right meaning.
   "A protected type is a limited type (see 7.5), and hence does not support
   copying nor predefined equality operators."
   is a bizarre mixture of precise terms ("predefined equality operators") and
   an undefined concept ("copying").
   "A protected type is a limited type (see 7.5), and hence does not support
   assignment_statements nor predefined equality operators."
   is over-specification (most other assignment operations are not allowed,
   either).

****************************************************************


Questions? Ask the ACAA Technical Agent