!standard 03.03.1(9) 06-01-12 AI95-00363/10 !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(16) !standard 04.08(6) !standard 05.02(16) !standard 08.05.1(5/1) !class amendment 03-11-26 !status Amendment 200Y 04-06-30 !status WG9 approved 04-11-18 !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 and 2, augmenting AI-168] Replace the second sentence of 3.7.1(7/1) with: ... However, in the case of an access subtype, a discriminant_constraint is illegal if the designated type has a partial view that is constrained or, for a general access subtype, has default_expressions 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 default_expressions for their discriminants. [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: - the designated subtype of A 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 the designated subtype of A 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 constrained 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 2] Delete 5.2(16) (a note) [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 its initial value (the null string in this case). That means that the attempt to assign a longer string raises Constraint_Error, even though the presence 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 anomaly is eliminated. !corrigendum 3.3.1(9) @drepl If a composite object declared by an @fa 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 @i. 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 @i. @dby If a composite object declared by an @fa 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 @i. 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 @i. !corrigendum 3.6(11) @ddel Within the definition of a nonlimited composite type (or a limited composite type that later in its immediate scope becomes nonlimited @emdash see 7.3.1 and 7.5), if a @fa contains the reserved word @b and the type of the component is discriminated, then the nominal subtype of the component shall be constrained. !corrigendum 3.7.1(7/1) @drepl A @fa is only allowed in a @fa whose @fa 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 @fa is illegal if there is a place within the immediate scope of the designated subtype where the designated subtype's view is constrained. @dby A @fa is only allowed in a @fa whose @fa denotes either an unconstrained discriminated subtype, or an unconstrained access subtype whose designated subtype is an unconstrained discriminated subtype. However, in the case of an access subtype, a @fa is illegal if the designated type has a partial view that is constrained or, for a general access subtype, has @fas 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 @fas for their discriminants. !corrigendum 3.10(9) @drepl A view of an object is defined to be @i if it is defined by an @fa or @fa with the reserved word @b, 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 @fa 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 @fa has discriminants, the object is constrained, either by the designated subtype, or by its initial value. @dby A view of an object is defined to be @i if it is defined by an @fa or @fa with the reserved word @b, 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) @drepl @xbullet @dby @xbullet !corrigendum 3.10.2(27/1) @drepl @xinbull is a named access type and @i is a tagged type, then the type of the view shall be covered by @i; if @i is anonymous and @i is tagged, then the type of the view shall be either @i'Class or a type covered by D; if @i is untagged, then the type of the view shall be @i, and @i's designated subtype shall either statically match the nominal subtype of the view or be discriminated and unconstrained;> @dby @xinbull is a named access type and @i is a tagged type, then the type of the view shall be covered by @i; if @i is anonymous and @i is tagged, then the type of the view shall be either @i'Class or a type covered by @i; if @i is untagged, then the type of the view shall be @i, and either:> @xi2bull shall statically match the nominal subtype of the view; or> @xi2bull<@i shall be discriminated in its full view and unconstrained in any partial view, and the designated subtype of @i shall be unconstrained.> !corrigendum 4.06(12.1/1) @drepl @xbullet @dby @xbullet !corrigendum 4.06(16) @drepl @xbullet @dby @xbullet @xinbull @xinbull !corrigendum 4.08(6) @drepl If the designated type of the type of the @fa 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). @dby If the designated type of the type of the @fa 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 5.02(16) @ddel @xindent<@s9<3 The values of the discriminants of an object designated by an access value cannot be changed (not even by assigning a complete value to the object itself) since such objects are always constrained; however, subcomponents of such objects may be unconstrained.>> !corrigendum 8.5.1(5/1) @drepl 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 @fa 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. @dby 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 @fa 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. ****************************************************************