Version 1.6 of ais/ai-00363.txt

Unformatted version of ais/ai-00363.txt version 1.6
Other versions for file ais/ai-00363.txt

!standard 03.03.1(9)          04-06-30 AI95-00363/04
!standard 03.06(11)
!standard 03.07.01(7/1)
!standard 03.10(9)
!standard 03.10.2(26)
!standard 03.10.2(27)
!standard 04.06(12.1/1)
!standard 04.06(14)
!standard 04.06(16)
!standard 04.08(6)
!standard 08.05.1(5/1)
!class amendment 03-11-26
!status Amendment 200Y 04-06-30
!status ARG Approved 8-0-1 04-06-14
!status work item 03-11-26
!status received 03-11-15
!priority High
!difficulty Hard
!subject Eliminating access subtype problems
!summary
Disallow access subtypes for general access types that have defaults for their discriminants, or for any access type that completes a private view that has no discriminants. Allow for unconstrained heap objects when the private view has no discriminants.
!problem
Normally when a discriminated type has defaults, objects of the unconstrained subtype allow their discriminants to change as part of a whole-object assignment.
Unfortunately, the possibility of constrained access subtypes means that objects that might be pointed-to by a value of such a subtype must be constrained, since the constraint check associated with such a subtype is performed only on assignment and parameter passing, not on use.
When we added general access types and objects explicitly declared "aliased" in Ada 95, we permitted access subtypes on these types as well, and generalized what was originally a requirement only on objects created by allocators, to apply to all aliased objects, whether declared or the result of an allocator, as well as all aliased components.
This has created a stream of headaches, particularly related to aliased components, because composite assignments and view conversions generally do no per-component constraint checks, so extra care needs to be used to prevent the possibility of discriminants changing due to operations on the enclosing object. See the discussion for details on this stream of headaches...
Another somewhat related problem, which was exacerbated by an Ada 95 change, is that if a private type is implemented with a full type with defaulted discriminants, then the implementation might work great in tests that don't put such objects in the heap, but would suddenly start failing if a user of the private type decided to allocate objects of the type in the heap. This was exacerbated in Ada 95 because we made it easier for a private type with no visible discriminant part to be implemented with a discriminant-with-defaults full type. This was possible in Ada 83, but required jumping through a few extra hoops.
!proposal
Part 1:
Disallow the discriminant constraint on a general access type if the discriminants have defaults on the designated type, recheck in an instance and presume the worst in a generic body.
Part 2:
For a private type with no visible discriminants, completed by a discriminated type with defaults, allow heap objects to be unconstrained. For such types, we must disallow both general and pool-specific access subtypes. The 'Constrained attribute would be False when dereferencing values of such an access type. There would need to be rules disallowing a 'Access that delivers a value of such a type being applied to a constrained variable (3.10.2(27)), and disallowing conversion from some other access type that had constrained designated objects (4.6(16)). Essentially the requirement for statically matching designated subtypes would carry over if there is a partial view that is not visibly unconstrained.
!wording
[part 1] Drop AI-295 -- it was strictly related to access subtype concerns.
[part 1] Drop AI-275
NOTE: The change proposed by AI-275 to 12.5.3(8) is no longer justified based on concerns about constrained access subtypes. However, the change might still be justified if we were concerned about the effect on optimizers if an array with aliased components were "viewed" as an array without aliased components, or vice-versa. However, this is only a concern in the presence of shared generics, and in that case, this is a probably a negligible extra overhead. Hence, we recommend that AI-275 be dropped, and the changes associated with AI-168 (which are already in RM2000) be "backed out."
[part 1] Modify 3.3.1(9):
... or the object is constant [or aliased (see 3.10)] the actual subtype ... [In the case of an aliased object, this initial value may be explicit or implicit; in the other cases, an explicit initial value is required.] ...
[part 1] Delete 3.6(11).
[part 1, augmenting AI-168] Replace the second sentence of 3.7.1(7/1) with:
... However, in the case of a general access subtype, a discriminant_constraint is illegal if the designated type has defaults for its discriminants. In addition to the places where Legality Rules normally apply (see 12.3), these rules apply also in the private part of an instance of a generic unit. In a generic body, this rule is checked presuming all formal access types of the generic might be general access types, and all untagged discriminated formal types of the generic might have defaults.
[part 1] In 3.10(9), delete the last two sentences (starting with "If the view defined by an object_declaration is aliased...").
AARM NOTE: The rules about allocated objects being constrained by their initial values are moved to 4.8.
[part 1] Modify 3.10.2(26):
... unless this subtype is indefinite, or the variable is [aliased] {constrained by its initial value}.
[part 2] Replace last part of 3.10.2(27) with:
... if D is untagged, then the type of the view shall be D, and either:
- A's designated subtype shall statically match the nominal subtype
of the view; or
- D shall be discriminated in its full view and unconstrained in any
partial view, and A's designated subtype shall be unconstrained.
[part 1, relaxing AI-168] Replace 4.6(12.1/1) with:
* In a view conversion, if the target type has aliased components, then so shall the operand type.
[part 2] Replace 4.6(16) with:
* If the target designated type is not tagged, then the designated types shall be the same, and either:
- the designated subtypes shall statically match; or - the designated type shall be discriminated in its full view and
unconstrained in any partial view, and the target designated subtype shall be unconstrained.
AARM Note: This does not require that types have a partial view in order
to allow the conversion, simply that any partial view that does exist is unconstrained.
[part 2] Replace 4.8(6) with:
If the designated type of the allocator is elementary, then the subtype of the created object is the designated subtype. If the designated type is composite, then the subtype of the created object is the designated subtype when the designated subtype is constrained or there is a partial view of the designated type that is constrained; otherwise, the created object is constrained by its initial value (even if the designated subtype is unconstrained with defaults).
AARM NOTE: If there is a constrained partial view of the type, this
allows the objects to be unconstrained. This eliminates privacy breaking (we don't want the objects to act different simply because they're allocated). Such a created object is effectively contrained by its initial value if the access type is an access-to-constant type, or the designated type is limited (in all views), but we don't need to state that here. It is implicit in other rules. Note, however, that a value of an access-to-constant type can designate a variable object via 'Access or conversion, and the variable object might be assigned by some other access path, and that assignment might alter the discriminants.
[part 1] Modify 8.5.1(5/1):
... unless this subtype is indefinite, or the variable is [aliased] {constrained by its initial value}. ...
!discussion
Allowing constrained access subtypes of general access types has created a stream of headaches, related to aliased components having their discriminants changed.
This issue led to 3.6(11) which requires aliased components of non-limited types to be constrained. However, if the type is private and its discriminants are only visible in the private part, then 3.6(11) provides no protection, so 3.7.1(7) was created to disallow general access subtypes of types whose partial view lacks discriminants, while the full view has defaulted discriminants.
Furthermore, view conversions of limited array types could create a problem, if the target and source type differed in whether the components were aliased, since in one view the discriminants were mutable, and in the other they weren't. This led to AI-168 and changes to 4.6(12).
generic formal (limited) array types create similar problems, if the actual and formal differ on component aliasing, for essentially the same reason. 12.5.3(8) worries about the case in one direction, and AI-275 was created to worry about the case in the other direction.
It was also noticed that 3.6(11) does not provide protection for generic formal private types, since the actual might have defaulted discriminants, so AI-295 was created to deal with this situation, essentially requiring 3.6(11) to be enforced in the private part of the instance, and enforced in the body of the instance by a (pseudo) run-time check.
The second problem addressed by this AI is the fact that a private type with no visible discriminants can be completed by a type with defaulted discriminants. Objects of such a type become constrained when allocated in the heap or declared as aliased.
Sofcheck reported that it hit some of their customers, because they had implemented Unbounded_Strings with a variant record. (One variant for "short" strings, and one variant for longer strings.) It is easy enough to solve by wrapping the full type in a record, but it seems a clear violation of the "spirit" of privateness, namely that objects of a private type with no visible discriminants should suddenly behave differently when clients of the type happen to allocate them in the heap.
It seems clear that fixing this problem would require allowing unconstrained heap objects to be created, at least when using an access type declared outside the scope of the full type, and disallowing access subtypes of such an access type, even if the type is pool-specific. Of course these access subtypes would be a bit weird, since they would have to be declared somewhere within the scope of the full type to be able to see the discriminants, even though the main access type were declared outside the scope of the full type.
!example
The Sofcheck example of the part 2 problem would look something like:
package Ada.Strings.Unbounded is type Unbounded_String is private; ... type String_Access is access all String; ... function To_Unbounded_String (Source : in String) return Unbounded_String; ... private Small_Max : constant := 20; type Unbounded_String (Small : Boolean := True) is record case Small is when True => Length : Natural := 0; Data : String (1..Small_Max); when False => Ptr : String_Access; end case; end record; end Ada.Strings.Unbounded;
package body Ada.Strings.Unbounded is function To_Unbounded_String (Source : in String) return Unbounded_String is begin if Source'Length <= Small_Max then return (Small => True, Length => Source'Length, Data => Source & (Source'Length+1..Small_Max => ' ')); else return (Small => False, Ptr => new String'(Source)); end if; end To_Unbounded_String; ... end Ada.Strings.Unbounded;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; procedure Test is type Access_Unbounded is access Unbounded_String; UStr : Unbounded_String; AStr : Access_Unbounded := new Unbounded_String; begin UStr := To_Unbounded_String ("A string longer than Small_Max"); -- OK; the discriminant can be changed. AStr.all := To_Unbounded_String ("A string longer than Small_Max"); -- Raises Constraint_Error in Ada 95; the discriminant cannot -- be changed. OK with the proposed changes. end Test;
Because the AStr object is allocated from a storage pool, Ada 95 says that it is constrained by it's initial value (the null string in this case). That means that the attempt to assign a longer string raises Constraint_Error, even though the presense of the discriminants are unknown to the client. The proposed changes allow unconstrained allocated objects so long as they have a constrained partial view (as in this example), so this anomoly is eliminated.
!corrigendum 3.3.1(9)
Replace the paragraph:
If a composite object declared by an object_declaration has an unconstrained nominal subtype, then if this subtype is indefinite or the object is constant or aliased (see 3.10) the actual subtype of this object is constrained. The constraint is determined by the bounds or discriminants (if any) of its initial value; the object is said to be constrained by its initial value. In the case of an aliased object, this initial value may be either explicit or implicit; in the other cases, an explicit initial value is required. When not constrained by its initial value, the actual and nominal subtypes of the object are the same. If its actual subtype is constrained, the object is called a constrained object.
by:
If a composite object declared by an object_declaration has an unconstrained nominal subtype, then if this subtype is indefinite or the object is constant the actual subtype of this object is constrained. The constraint is determined by the bounds or discriminants (if any) of its initial value; the object is said to be constrained by its initial value. When not constrained by its initial value, the actual and nominal subtypes of the object are the same. If its actual subtype is constrained, the object is called a constrained object.
!corrigendum 3.6(11)
Delete the paragraph:
Within the definition of a nonlimited composite type (or a limited composite type that later in its immediate scope becomes nonlimited -- see 7.3.1 and 7.5), if a component_definition contains the reserved word aliased and the type of the component is discriminated, then the nominal subtype of the component shall be constrained.
!corrigendum 3.7.1(7/1)
Replace the paragraph:
A discriminant_constraint is only allowed in a subtype_indication whose subtype_mark denotes either an unconstrained discriminated subtype, or an unconstrained access subtype whose designated subtype is an unconstrained discriminated subtype. However, in the case of a general access subtype, a discriminant_constraint is illegal if there is a place within the immediate scope of the designated subtype where the designated subtype's view is constrained.
by:
A discriminant_constraint is only allowed in a subtype_indication whose subtype_mark denotes either an unconstrained discriminated subtype, or an unconstrained access subtype whose designated subtype is an unconstrained discriminated subtype. However, in the case of a general access subtype, a discriminant_constraint is illegal if the designated type has defaults for its discriminants. In addition to the places where Legality Rules normally apply (see 12.3), these rules apply also in the private part of an instance of a generic unit. In a generic body, this rule is checked presuming all formal access types of the generic might be general access types, and all untagged discriminated formal types of the generic might have defaults.
!corrigendum 3.10(9)
Replace the paragraph:
A view of an object is defined to be aliased if it is defined by an object_declaration or component_definition with the reserved word aliased, or by a renaming of an aliased view. In addition, the dereference of an access-to-object value denotes an aliased view, as does a view conversion (see 4.6) of an aliased view. Finally, the current instance of a limited type, and a formal parameter or generic formal object of a tagged type are defined to be aliased. Aliased views are the ones that can be designated by an access value. If the view defined by an object_declaration is aliased, and the type of the object has discriminants, then the object is constrained; if its nominal subtype is unconstrained, then the object is constrained by its initial value. Similarly, if the object created by an allocator has discriminants, the object is constrained, either by the designated subtype, or by its initial value.
by:
A view of an object is defined to be aliased if it is defined by an object_declaration or component_definition with the reserved word aliased, or by a renaming of an aliased view. In addition, the dereference of an access-to-object value denotes an aliased view, as does a view conversion (see 4.6) of an aliased view. Finally, the current instance of a limited type, and a formal parameter or generic formal object of a tagged type are defined to be aliased. Aliased views are the ones that can be designated by an access value.
!corrigendum 3.10.2(26)
Replace the paragraph:
by:
!corrigendum 3.10.2(27/1)
Replace the paragraph:
by:
!corrigendum 4.06(12.1/1)
Replace the paragraph:
by:
!corrigendum 4.06(14)
Delete the paragraph:
!corrigendum 4.06(16)
Replace the paragraph:
by:
!corrigendum 4.08(6)
Replace the paragraph:
If the designated type of the type of the allocator is elementary, then the subtype of the created object is the designated subtype. If the designated type is composite, then the created object is always constrained; if the designated subtype is constrained, then it provides the constraint of the created object; otherwise, the object is constrained by its initial value (even if the designated subtype is unconstrained with defaults).
by:
If the designated type of the type of the allocator is elementary, then the subtype of the created object is the designated subtype. If the designated type is composite, then the subtype of the created object is the designated subtype when the designated subtype is constrained or there is a partial view of the designated type that is constrained; otherwise, the created object is constrained by its initial value (even if the designated subtype is unconstrained with defaults).
!corrigendum 8.5.1(5)
Replace the paragraph:
The renamed entity shall not be a subcomponent that depends on discriminants of a variable whose nominal subtype is unconstrained, unless this subtype is indefinite, or the variable is aliased. A slice of an array shall not be renamed if this restriction disallows renaming of the array. In addition to the places where Legality Rules normally apply, these rules apply also in the private part of an instance of a generic unit. These rules also apply for a renaming that appears in the body of a generic unit, with the additional requirement that even if the nominal subtype of the variable is indefinite, its type shall not be a descendant of an untagged generic formal derived type.
by:
The renamed entity shall not be a subcomponent that depends on discriminants of a variable whose nominal subtype is unconstrained, unless this subtype is indefinite, or the variable is constrained by its initial value. A slice of an array shall not be renamed if this restriction disallows renaming of the array. In addition to the places where Legality Rules normally apply, these rules apply also in the private part of an instance of a generic unit. These rules also apply for a renaming that appears in the body of a generic unit, with the additional requirement that even if the nominal subtype of the variable is indefinite, its type shall not be a descendant of an untagged generic formal derived type.
!ACATS test
ACATS test(s) should be constructed to check these changes.
!appendix

From: Tucker Taft
Sent: Saturday, November 15, 2003  5:30 PM

Along with Pascal, I was assigned homework to study the problem of access
subtypes.  Here are my thoughts:

First, let's identify the basic problem and sequence of
piecemeal solutions:

-------------------------------------------------------
---- the problem and current solution attempts --------
-------------------------------------------------------

Normally when a discriminated type has defaults, objects of
the unconstrained subtype allow their discriminants to
change as part of a whole-object assignment.

Unfortunately, the *possibility* of constrained access subtypes
means that objects that might be pointed-to by a value of
such a subtype must be constrained, since the constraint check
associated with such a subtype is performed only on assignment
and parameter passing, not on use.

When we added general access types and objects explicitly declared "aliased"
in Ada 95, we permitted access subtypes on these types as well, and
generalized what was originally a requirement only on objects created by
allocators, to apply to all aliased objects, whether declared or
the result of an allocator, as well as all aliased components.

This has created a stream of headaches, particularly related to
aliased components, because composite assignments and view
conversions generally do no per-component constraint checks, so
extra care needs to be used to prevent the possibility of
discriminants changing due to operations on the enclosing
object.  This has led to 3.6(11) which requires aliased components
of non-limited types to be constrained.  However, if the type
is private and its discriminants are only visible in the private part,
then 3.6(11) provides no protection, so 3.7.1(7) was created to
disallow general access subtypes of types whose partial view
lacks discriminants, while the full view has defaulted discriminants.

Furthermore, view conversions of limited array types could
create a problem, if the target and source type differed
in whether the components were aliased, since in one view
the discriminants were mutable, and in the other they weren't.
This led to AI-168 and changes to 4.6(12).

Generic formal (limited) array types create similar problems,
if the actual and formal differ on component aliasing, for essentially
the same reason.  12.5.3(8) worries about the case in one direction,
and AI-275 was created to worry about the case in the other direction.

It was also noticed that 3.6(11) does not provide protection for
generic formal private types, since the actual might have
defaulted discriminants, so AI-295 was created to deal with this
situation, essentially requiring 3.6(11) to be enforced in the
private part of the instance, and enforced in the body of the
instance by a (pseudo) run-time check.

Another somewhat unrelated problem, which was exacerbated by
an Ada 95 change, is that if a private type is implemented
with a full type with defaulted discriminants, then the
implementation might work great in tests that don't put
such objects in the heap, but would suddenly start failing
if a user of the private type decided to allocate objects
of the type in the heap.  This was exacerbated in Ada 95
because we made it easier for a private type with no
visible discriminant part to be implemented with a
discriminant-with-defaults full type.  This was possible
in Ada 83, but required jumping through a few extra hoops.

I know about this problem because a few years ago it hit
some of our customers, because we had implemented Unbounded_Strings
with a variant record, one variant for "short" strings, and
one variant for longer strings.  It is easy enough to solve
by wrapping the full type in a record, but it seems a clear
violation of the "spirit" of privateness, namely that objects
of a private type with no visible discriminants should suddenly
behave differently when clients of the type happen to allocate
them in the heap.

It seems clear that fixing this problem would require allowing
unconstrained heap objects to be created, at least when using
an access type declared outside the scope of the full type,
and disallowing access subtypes of such an access type, even
if the type is pool-specific.  Of course these access subtypes
would be a bit weird, since they would have to be declared
somewhere within the scope of the full type to be able to
see the discriminants, even though the main access type were
declared outside the scope of the full type.

------------------------------------------
---- So where do we go from here? --------
------------------------------------------

The "fix" in 3.7.1(7) which disallows general access subtypes
in certain cases is a bit frustrating, in that it is really just
an attempt to patch a hole in 3.6(11), and doesn't really help
with the problem I described above about private types that stop
working for heap objects.  Unless you know that there are *no*
access subtypes, whether pool-specific or general, can you
start expecting an allocator for a private type with no visible
discriminants to create an object that "works" the same way
as a declared object or component having the same type.
So personally, I would put some priority on finding a
solution that fixes this privacy-breaking situation.
(I'll come back to this later -- see below.)

In addition, the language rules are excessively complicated due to the
possibility of general access subtypes designating types, private or
not, that have defaulted discriminants.  The rules would be significantly
simplified if declared objects and components behave the same whether
aliased or not, as far as constrained-ness.  For these cases,
we are only worried about general access subtypes, since only
general access types can designate (aliased) declared objects or components.

In the discussion of AI-295, one of the alternatives considered was
disallowing creating access subtypes of general access types, perhaps
only if the designated type has defaulted discriminants.  However, this was
felt to be a contract model problem, since a formal *pool-specific*
access type can be associated with an actual *general* access type.
A related problem is that a formal type without defaults can
be associated with an actual type that has defaults.
Let's look at this problem in more detail:

The contemplated rule (in place of the existing rule in 3.7.1(7)) is:
  A discriminant constraint is not permitted on a general access type
  if the designated type has defaulted discriminants.

To enforce this rule in the presence of generics, we would clearly
need to check it in instances, including in the private part.
In the body, we would have to disallow a constraint on any access type
that *might* have this property, or do it as a run-time check.
None of these rules seem too bad, since the likelihood of declaring
access subtypes inside a generic body are quite slim, and if
subtypes of some access type are really going to be used widely, it may be
reasonable to omit the defaults on the discriminants to begin with.

One other alternative to consider is to allow such access subtypes
(perhaps only in generic bodies), but define their semantics to
be that the checks are, as now, made on assignment and parameter passing,
but that, in addition, discriminant checks are also required on use.  On use,
the implementation would be permitted to check for an exact
match to the associated discriminant constraint, or to perform
just the "normal" discriminant checks associated with referencing
discriminant-dependent components.

In fact, probably no wording changes related to discriminant checks
would necessarily be required to adopt this "alternative" semantic model
(other than perhaps to permit the "fiercer" check), since discriminant
checks are defined to occur on every use already, and it is just
logical inference which allows compilers to currently omit the checks
when dereferencing a value of a constrained access subtype.
This logical inference would no longer be true in all cases, if
we permit aliased declared objects and components to have mutable
discriminants.

Clearly disallowing the access subtypes themselves is preferable
from the point of view of doing as much checking as possible at
compile-time, but the "altered" semantic model would actually be a
bit more upward compatible, since existing exception-free code
would remain legal and exception-free.  Code that is currently
raising exceptions might raise them at different places, namely
on use of an access value, rather than on an attempt to change
the discriminants via assignment.

My recommendation?

Disallow the discriminant constraint on a general
access type if the discriminants have defaults on the
designated type, recheck in an instance and presume the worst
in a generic body.  I think this is quite reasonable since
declaring access subtypes is rare to begin with, even rarer when
the designated type's discriminants have defaults, rarer still
in a package body, and probably exceedingly rare in a generic package body.
(If by chance some upward incompatilibility does arise, two "workarounds"
are to eliminate the defaults on the discriminants (if not really needed),
or eliminate the constraints on the declaration of the access subtypes.
Exception-free code should continue to run exception-free.)

This change would allow us to eliminate 3.6(11), and the various
attempts to "patch" it via AI-168 for 4.6(12), AI-275 for 12.5.3(8),
and AI-295 for generics.  Aliased components would no longer
have to be constrained; we could independently decide whether
standalone aliased objects declared with defaulted discriminants would
be constrained by their initial value, though there doesn't seem much
remaining justification for making them so.

-----------------------------------------------------
---- fixing privacy-breaking constrainedness --------
-----------------------------------------------------

As mentioned above, an existing error-prone situation
is that a private type that works perfectly well for
non-aliased objects, starts failing on objects declared
aliased or allocated in the heap.  The "aliased" part could
be fixed by the above change, but the heap-allocated problem is
not fixed by disallowing constraints on certain general access subtypes.

To fix this problem, we must disallow constraints on *all* access types
declared outside the package defining the private type (presuming the
private view has no visible discriminants), allocating
space for unconstrained objects in the heap even when
constraints are given in the allocator for such an access type,
and setting the 'Constrained attribute False when dereferencing
values of such an access type.  Essentially it makes an allocator
for such an access type equivalent to declaring a variable
of the (private) type, which is always going to have mutable
(non-visible) discriminants.

In fact, I believe that constraints are *already* disallowed on
such an access type, even when inside the scope of the full
type of the designated type, because according to 3.10(14), such
an access type is constrained from the get-go.  This is because
at the point of its definition, and throughout its immediate scope,
its designated type never has any discriminants, so there is
nothing to constrain.  As usual, even if some client
knows more than what is known where the access type is declared,
it can't take advantage of that.

So the real change is to make the semantics of allocators (see 4.8(6)) match
this model as well.  Allocators for such an access type should
create unconstrained objects, even if the place where the allocator
takes place might "know" that the designated type has discriminants,
and might use a constrained subtype in the allocator.

If the access type is a general access type, and we presume the "other"
change proposed above is adopted, then this "new" semantics for allocators
should probably apply to access types declared inside the package
as well, presuming the designated subtype is not constrained,
since subtypes would be disallowed on the "inside" general access types
as well.  Furthermore, there would need to be rules disallowing
a 'Access that delivers a value of such a type being applied to a
constrained variable (3.7.2(27)), and disallowing conversion from some other
access type that had constrained designated objects (4.6(16)).
Both 3.7.2(27) and 4.6(16) would then say "discriminated and indefinite"
rather than "discriminated and unconstrained."

I think this further change would be a "good" thing, as it provides
significant improvements to the privacy model, and it complements
the rule suggested which applies only to general access types.

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

From: Pascal Leroy
Sent: Monday, November 24, 2003  9:17 AM

Thank you for writing this up, and sorry for the late answer.

I very much like your proposal, in particular the notion of fixing the
privacy-breaking oddity where objects of a private type work differently
depending on whether they are allocated on the heap on declared as normal
variables/constants.

I think that change #1 (discriminant constraints for general access types) is
fairly uncontroversial as this area of the language is quite muddled, so it's
only going to break code which was only working by a fluke.  And the effect of
the change will be to make existing code illegal.

On the other hand change #2 (the privacy-breaking problem) is more dicey as it
introduces a performance incompatibility: imagine a unit that was allocating
1,000,000 objects of 10 bytes, but would occasionally allocate an object of
1,000,000 bytes.  Suddenly it's going to allocate 1,000,000 objects of
1,000,000 bytes.

I am still willing to swallow this incompatibility given the headaches that
it's going to solve, but it's definitely going to be a contentious proposal.

I will include a summary of your proposal in my next WG9 report.  In all
likelihood it will go back to the ARG for further analysis.

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

From: Tucker Taft
Sent: Monday, November 24, 2003  1:41 PM

> ...
>
> I think that change #1 (discriminant constraints for general access types) is
> fairly uncontroversial as this area of the language is quite muddled, so it's
> only going to break code which was only working by a fluke.  And the effect
> of the change will be to make existing code illegal.

Once I sat down and began to piece together the set of patches we made to
try to fix the problems with 3.6(11), I was somewhat appalled.  I hope
we can at least get consensus on this part of the proposal.

>
> On the other hand change #2 (the privacy-breaking problem) is more dicey as
> it introduces a performance incompatibility: imagine a unit that was
> allocating 1,000,000 objects of 10 bytes, but would occasionally allocate an
> object of 1,000,000 bytes.  Suddenly it's going to allocate 1,000,000 objects
> of 1,000,000 bytes.

Yes, that is an issue.

I see this problem as relatively rare, since from the outside view, this type
only comes in one size, and all local or global variables are going to be the
"max" size (for most run-time models).  There are also a couple of potential
workarounds:

  1) Declare the (full and private) type limited -- I would presume only
     nonlimited types would have unconstrained instances in the heap.
  2) Put discriminants, known or unknown, on the private type declaration.

> I am still willing to swallow this incompatibility given the headaches that
> it's going to solve, but it's definitely going to be a contentious proposal.

Probably...

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

From: Randy Brukardt
Sent: Wednesday, April 21, 2004  5:25 PM

The problem with part 2 of AI-363 (as discussed at Phoenix) is that there
was performance incompatibilities. (This part says that certain types are
allocated as unconstrained on the heap, in order to avoid privacy breaking.)
I've been thinking about a related problem, which is implementing the
containers library as specified. That specification prevents the use of
constrained elements for the definite packages, which means that they can't
allocated from the heap individually or be aliased.

Because of Janus/Ada's code sharing, each component of a generic private
type is allocated individually by the compiler. That allocation is of course
unconstrained. I'd like to replace that by an explicit allocation (so that
we can take advantage of moving the pointers instead of the elements for
inserts and sorts), but to do so, I have to have a way to allocate
unconstrained objects on the heap. The only technical reason that I can't do
that is that Ada doesn't allow it (ignoring the issue of access subtypes,
which is taken care of by AI-363, part 1, which had general support).

So I was thinking of adding a representation attribute to our compiler:

    type A_Ptr is access all Element_Type;
    for A_Ptr'Designates_Unconstrained use True;

All this actually does in Janus/Ada is change the value of the 'Constrained
flag passed to assignment thunks. (A very easy change.) This would just be
supported on definite subtypes, it makes no sense for indefinite subtypes.

This seems like a generally useful idea (esp. in the absence of AI-363 pt.
2). It doesn't really help with private types, but a similar idea could be
applied there:

    type Some_Private is private;

    type Some_Private (Disc : Integer := 100) is record ...
    for Some_Private'Unconstrained_when_Allocated use True;

which would avoid compatibility problems. The only annoyance is the usual
problem: you'd prefer this to be the default case. Of course, with more
incompatibility, we could make this the default, and then use the attribute
to force back to the old way if there is trouble.

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

From: Tucker Taft
Sent: Saturday, June 5, 2004 11:48 AM

Here is a new version of AI-363 having to do with
access subtypes. [Editor's note: This is version /03.] I have identified
each wording change as either "part 1" or "part 2" where
part 1 is eliminating general access subtypes, while
part 2 relates to private types completed with
discriminated-with-defaults full types.

I recommend we do both.  I have been careful to make part 2
as compatible as possible.  Essentially what it does is
require static matching of designated subtypes for 'Access
and conversions for access-to-private when the private
type has no visible discriminants, even if the full view does
have discriminants.  This allows objects of such a private
type to be unconstrained in the heap.  By part 1, such
objects would already be unconstrained if declared "aliased."

I think this will fix some nasty privacy breakages in the
language, as well as simplify it overall.  Adding "aliased"
to a declaration will now have many fewer subtle semantic
and legality effects.

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

Questions? Ask the ACAA Technical Agent