!topic LSN029 on Analysis of Upward Compatiblity/Consistency $Revision: 1.2 $ !from Tucker Taft $Date: 92/03/02 14:19:43 $ !discussion Here is a long overdue analysis of upward compatibility of Ada 9X from Ada 83. We will include this in the forthcoming Mapping Rationale document for version 4.0, so that it will be widely available. As usual, comments are welcome... -Tuck ======================= ANALYSIS OF Ada 9X UPWARD COMPATIBILITY/CONSISTENCY First, here is some terminology: Upward Compatible: A proposed change is upward compatible if the semantics of all legal Ada 83 programs are unaffected by the change. In particular, all legal Ada 83 programs remain legal, with the same meaning. Upward Consistent: (UCon) A proposed change is upward consistent if the semantics of all legal Ada 83 programs that remain legal are unaffected by the change. Some legal Ada 83 programs may become illegal, but those that remain legal have the same meaning. Upward Consistent if no Constraint Errors: (UCINCE) A proposed change to a given feature is "upward consistent if no constraint errors" (UCINCE) if the semantics of all legal Ada 83 programs that do not raise a constraint error relating to the feature and that remain legal, are unaffected by the change. Some legal Ada 83 programs may become illegal, and some that raised constraint error may no longer do so if a meaningful result can be produced. Upward Inconsistent: (Incon) A proposed change is upward inconsistent if the semantics of some legal, correct Ada 83 programs are affected by the change. In particular, the program may now raise a predefined exception when it didn't in the past, or it may produce a different result. We will not discuss changes that are upward compatible any further. For all of the others, the relevant questions include: a) How common are programs that will have the upward incompatibility? We give examples if necessary to illustrate the problem. b) How easy is the work-around? We give examples if necessary to illustrate the approach. c) Is there a work-around that works identically in both Ada-83 and Ada-9X? d) Are all instances of upward inconsistency detectable statically (i.e. is it a "warnable" upward inconsistency)? e) How serious is the change in effect estimated to be for a typical instance of the inconsistency? Only (c) and (d) are objective questions, (a), (b), and (e) depend on estimates, and so are hence subject to discussion and debate, and would benefit from systematic surveys of existing code. Please note we are trying to be exhaustive here, so we are mentioning even "marginal" cases. At the end we will try to summarize what we see as the "major" cases. Note also that some of these upward incompatibilities relate to features that may not be included in the ultimate Ada 9X, or that may appear in a different form that is more nearly upward compatible. In some cases we discuss possible changes that might improve the upward compatibility of a feature. Finally, note that we are not considering cases where Ada 9X limits implementation freedom relative to Ada 83, unless it is clear that the newly required behavior is noticeably different from the typical implementation behavior. We may produce a separate document to discuss implementation freedom issues at a later date. ================================ 1) MS-2;4.0: New reserved words -- Upward Consistent a) We estimate relatively few collisions with the new reserved words (aliased, protected, requeue, tagged, until). b) The work around is straightforward -- replace the identifier with a new one; recompile and see if there are any overloading problems; iterate if necessary. c) The work-around works identically in Ada-83 and Ada-9X d) Since the change is upward consistent, by definition any problems are detected at compile-time and the program is rejected. e) N/A 2) MS-3.3.2;4.0: Subtype accuracy constraints removed from the langauge -- Upward Consistent a) We estimate relatively few uses of subtype accuracy constraints. b) The work around is straightforward -- remove the accuracy constraint from the object, subtype, or derived type declaration; for a derived type, it may be preferred to define a new real type presuming there are no user-defined derivable subprograms; scan for uses of subtype-specific attributes whose value will now reflect the base-type attributes (see also changes to numeric model, below). c) The work-around works identically in Ada-83 and Ada-9x d) All cases rejected at compile-time e) N/A 3) MS-3.3.3;4.0: A derived type inherits all operations of the parent type declared immediately in the same list of declarations as the parent type -- Upward inconsistent a) We estimate that explicit type derivation is used relatively rarely in Ada 83. Furthermore, we estimate that deriving from a type in the same list of declarations as the parent type declaration is even rarer. Finally, we estimate that deriving from a parent type in the same list of declarations, after one of the parent's derivable operations has been overridden, with the expectation that one will get the non-overridden operation is even rarer still. Note that we are considering b) The work-around needed to get the non-overridden operations (presuming that is the intent) is straightforward: If the parent type is a derived type, derive from its parent. If the parent type is a numeric, array, or general access type, define a new numeric/array/general access type, with the same characteristics as the former parent type. (Numeric, array, and general access types are explicitly interconvertible like derivatives so long as they have compatible characteristics). If the parent type is some other kind of type, define a third type, make it the parent of the old parent type and the old derived type. (Note that this third method actually works for all types.) c) The work-around works identically in Ada 83 and Ada 9X. d) All potential inconsistencies are warnable. e) The effect of the inconsistency is that the derived type uses the direct parent's definition of an operation, rather than the predefined op or its "grand parent's." 4) MS-3.3.3;4.0: A type derivation is illegal if it preceeds the definition of the last primitive operation of the parent type -- Upward Consistent [Note: the restriction against adding primitive operations after a body is being dropped -- it was too restrictive. The wording of the remaining restriction against "early" derivation is inverted to place the "blame" on the type derivation rather than the definition of the primitive operation.] a) We estimate that explicit type derivation is used relatively rarely in Ada 83. Furthermore, deriving from a type in the same list of declarations is even rarer. Note that we are considering limiting this restriction to tagged types, in which case the (untagged) type derived before the last primitive operation has been defined would not inherit the operations defined later. This restriction would therefore not be an upward incompatibility. It would also open up another work-around for the upward inconsistency associated with inheriting operations from type defined outside a visible part, namely move the type derivation up to immediately following the parent's type declaration. b) The work-around is straightforward: Move the type derivation down to after the definition of the last primitive operation. Alternatively, the the above-mentioned workaround of introducing a third type to act as the parent of both previous types solves the problem, if the intent is to not inherit the parent type's operations. c) A work-around exists that works identically in Ada 83 and Ada 9X. d), e) N/A 5) MS-3.5.2;4.0: CHARACTER now has 256 positions -- Upward Inconsistent a) We estimate a medium number of cases where legal programs will become illegal; we estimate a small number of legal programs that will remain legal but have a different effect. An example of an inconsistent program is one that refers to CHARACTER'LAST, since this will now have a position number of 255 instead of 127. b) The work-around is straightforward, though not trivial -- Decide how to handle characters in the "upper half"; Add "others" clause to aggregates and case statements based on CHARACTER, or appropriate specific choices for interesting upper-half characters. c) There are work-arounds that will work identically in Ada-83 and Ada-9x presuming there are no upper-half characters in the input. d) The upward inconsistencies are warnable, since they are generally related to uses of CHARACTER'LAST. e) We estimate that the effect of the upward inconsistencies is relatively mild. Furthermore, most programs that have upward inconsistencies will also have upward incompatibilities, so they will be rejected at compile-time anyway. 6) MS-3.5.2;4.0: All character literals are now overloaded on both WIDE_CHARACTER and CHARACTER -- Upward consistent a) We estimate the number of cases that rely on the type uniqueness of characters to be very small. Here is an example of one: if 'a' = 'b' then ... This will resolve in Ada 83, but be ambiguous in Ada 9X. Note that we are not proposing to add WIDE_CHARACTER (or WIDE_STRING) operations to TEXT_IO, but rather put such operations in a separate WIDE_TEXT_IO package. Therefore, the following will remain unambiguous: use TEXT_IO; ... PUT('a'); ... PUT("hello"); b) The work-around is simple: Add a type qualification for CHARACTER, for example: if CHARACTER'('a') = 'b' then ... c) The work-around will work identically in Ada 83 and Ada 9X d), e) N/A 7) MS-3.5.2;4.0 and elsewhere: New identifiers in package STANDARD -- Upward inconsistent a) We estimate very few collisions with names added to package STANDARD; we estimate near zero number of cases where a program with a collision would be legal and inconsistent. For example, here is a case of an upward inconsistency: package MINE is type WIDE_CHARACTER is ('a', 'b'); end MINE; with MINE; use MINE; procedure TROUBLE is X : WIDE_CHARACTER := 'a'; -- In Ada 83, this uses MINE.WIDE_CHARACTER. -- In Ada 9X, STANDARD.WIDE_CHARACTER hides -- the use-visible MINE.WIDE_CHARACTER begin if WIDE_CHARACTER'POS(X) /= 0 then PUT_LINE("Inconsistent!"); else PUT_LINE("Consistent"); end if; end TROUBLE; The new identifiers currently proposed for STANDARD are: WIDE_CHARACTER WIDE_STRING INT_CLASS (ROOT_INTEGER_CLASS in our more recent thinking) REAL_CLASS (ROOT_REAL_CLASS in our more recent thinking) TASK_CLASS (ROOT_TASK_CLASS in our more recent thinking) b) The work-around is simple: Pick a different identifier and recompile (i.e., handle this similar to a reserved word). c) The work-around will work identically in Ada 83 and Ada 9X d) The potential upward inconsistencies are warnable, by flagging any declaration of an entity with a name that appears in Ada 9X STANDARD but not in Ada 83 STANDARD. e) We estimate the effect of any undetected inconsistencies to be relatively mild. Furthermore, as mentioned above, we expect that essentially all programs with collisions will be rejected at compile time, given the extremely small chance that the identifier in STANDARD is a legal stand-in for the previously use-visible identifier. 8) MS-3.5.4;4.0: Exceeding the FIRST and LAST attributes of an integer base type need not raise CONSTRAINT_ERROR -- UCINCE a) We estimate very few programs now rely on the raising of CONSTRAINT_ERROR on assignment to variables of an integer base type, so long as the correct answer is produced in the end. RM 11.6 already grants the freedom to perform evaluations in greater range than the base type. b) The work-around is straightforward -- Define and use a constrained subtype rather than an unconstrained integer base type. c) The work-around works identically in Ada 83 and Ada 9X d) The potential upward inconsistencies are warnable, since the implementation knows whenever it has allocated a "wider" register or storage cell for a variable of an integer base type. e) The effect of the upward inconsistency is to avoid a constraint error and continue further in an algorithm with a value outside of BASE'FIRST .. BASE'LAST. 9) MS-3.5.6;4.0: Exceeding the FIRST and LAST attributes of a real base type need not raise CONSTRAINT_ERROR -- UCINCE a) .. e) See (8) above. 10) MS-3.5.6;4.0: A range constraint on a real type (after its declaration) is a forcing occurrence for the type -- Upward consistent a) We estimate few programs specify rep clauses for real types after defining constrained subtypes or derived types of them. b) The work around is very simple -- Move the rep clause to before the declaration of constrained subtypes/derived types c) The work-around works identically in Ada 83 and Ada 9x. d), e) N/A 11) MS-3.5.9;4.0: Default choice for small need not be a power of 2 -- Upward inconsistent [NOTE: There is a missing section header for "Fixed Point Types."] a) We estimate relatively few programs contain a delta that is not a power of 2 and have no rep-clause for small. b) The work around is very simple -- Change the delta to equal the desired (binary) small. c) The work around will work identically in Ada 83 and Ada 9X. d) This upward inconsistency is warnable. e) The effect of the upward inconsistency will likely be an improvement in accuracy, and a decrease in performance. 12) MS-3.7.3;4.0: String literals now overloaded on STRING and WIDE_STRING -- Upward consistent a) We estimate very few programs depend on the resolution of the type of a string literal independent of context. Here is an example that is legal in Ada 83 but would be ambiguous in Ada 9X: if "abc" = "abc" then ... b) The work-around is simple: Use a type qualification for type STRING. c) The work-around works identically in Ada 83 and Ada 9X d), e) N/A 13) MS-4.5.3;4.0: Low bound of catenation always same as low bound of left operand -- Upward inconsistent a) We estimate that extremely few programs take advantage of the catenation of a null left operand taking its low bound from the right operand. Note that in many cases, the left operand of a concatenate cannot be null (because it is a non-null string literal or the result of 'IMAGE). In other cases, the left operand and right operand have the same low bound (e.g., 'IMAGE and STRING literals always have a low bound of 1. In other cases, sliding is performed on the result of the catenate, making the bounds irrelevant. b) The work-around is straightforward: Define an operation SLOW_CONCAT (;-) that mimics the Ada 83 low bound calculation. Use it where it is possible that the left operand might be null and its low bound might be different from the low bound of the right operand. c) The work-around will work identically for Ada 83 and Ada 9x. d) Any potential upward inconsistency is warnable. e) The effect of the upward inconsistency is to produce an array result with different bounds, but the same length. Because sliding happens in so many contexts, and 'RANGE is used in others, this will rarely cause a problem. 14) MS-4.5.7;4.0: Accuracy model simplified and moved to annex -- Upward Consistent (presuming EPSILON/SMALL/LARGE renamed to MODEL_EPSILON/SMALL/LARGE as anticipated) a) We estimate that relatively few programs (not including ACVCs) reference model attributes. b) The work-around is straightforward: The Ada 83 model attributes are all a function of F'MANTISSA (aka "B"), which is in turn a function of F'DIGITS. The references to the Ada 83 model attributes may be replaced with their respective formulas in terms of F'DIGITS. c) The work-around works identically in Ada 83 and Ada 9X. d), e) N/A. 15) MS-4.6;4.0: Implicit array subtype conversion (sliding) performed in more contexts -- UCINCE a) This change is upward compatible for programs that do not currently raise CONSTRAINT_ERROR due to an array bounds mismatch. The only effect of this change is that where a program before would raise constraint error if the bounds mismatched, it will now "slide" the array bounds. No constraint error will be raised if the array lengths match. We estimate that no programs take advantage of the current constraint error as part of their "correct" behavior. b) There is no apparent need for a work-around. However, if there is an interest in checking bounds prior to sliding, an explicit subtype membership test may be inserted. c) An explicit subtype membership test will work identically in Ada 83 and Ada 9X. d) All possible inconsistencies are warnable. e) We estimate the effect of the inconsistency to be very minor, since the value after sliding must still have the correct length. 16) MS-7.4.5;4.0: Function return of limited type defers finalization -- Upward Inconsistent a) We estimate that extremely few, if any, functions that return limited types have local tasks that are still active when the return statement is reached. In Ada 83, the function will wait after calculating the return value, but before returning to the caller, for the local task to terminate. In Ada 9X the function will return immediately, and then the caller will wait for the task to terminate after finishing using the return value. b) If any such functions do in fact exist, the local task can presumably be declared in a nested block to ensure that it is terminated prior to the function return. However, as mentioned above, we doubt that any such functions exists (outside of the ACVC suite). c) A nested block will work identically in Ada 83 and Ada 9X. d) All possible inconsistencies are warnable. e) We estimate the effect of the inconsistency to be very minor, first because we doubt that any such functions exist, and second, because in both Ada 83 and Ada 9X the wait for local task termination occurs after evaluating the return value. So there is no way to take advantage of the terminated state of the local task, even in Ada 83. 17) MS-8.3;4.0: All primitive operators and character literals are "primitively visible" when not otherwise directly visible -- Upward Consistent a) We estimate that very few programs will become ambiguous due to the primitive visibility of operators and character literals. To become ambiguous, the program must use a non-primitive (and hence user-defined) operator that is only use-visible, in a way that is ambiguous with some primitive (predefined) operator. Note that a non-primitive operator that is a rename of a primitive operator hides the primitive operator, when the renaming is use-visible. Therefore, an "operators" package containing renames of primitive operators may be "use"d without creating ambiguity with the corresponding primitive operators. b) The work-around is straightforward: Use selected component notation for the non-primitive operator, or an explicit rename. In Ada 9X, one may explicitly "hide" the predefined operator with "is <>" if it should never be visible (see MS-6.1;4.0). c) The selected-component or explicit rename work-around works identically in Ada 83 and Ada 9X. d), e) N/A 18) MS-8.7;4.0: Preference for root (universal) numeric operators, rather than against implicit conversion -- Upward consistent a) We estimate that extremely few programs will become ambiguous due to the revised preference rule. Here is an example: function "+"(Left, Right : My_Integer) return Integer; ... Some_Integer(3 + 5) ... The operand "3 + 5" would resolve in Ada 83 to universal int, even though this weird "+" is immediately visible. In Ada 9X, this would be ambiguous because this weird "+" is not a primitive operator corresponding to a universal-int operator. b) The work-around is straightforward: Insert one or more type or package qualifications to remove ambiguity. c) The work-around works identically in Ada 83 and Ada 9X. d), e) N/A 19) MS-10.1;4.0: Body required for library unit package -- Upward Consistent a) We estimate that relatively many library unit packages exist that lack a body. b) The work-around is straightforward: At the end of the source file containing the package spec, including a (null) package body. c) The work-around works identically in Ada 83 and Ada 9x d), e) N/A 20) MS-10.5;4.0: Pragma ELABORATE applies to the closure of bodies reachable from the specified library unit -- Upward Consistent a) We estimate that a small number of programs will be rejected because of an indirect circularity of pragma ELABORATEs. Note we are considering other approaches to improving control over elaboration order. One simple alternative is to allow indirect circularities, in which case an implementation-defined order for the bodies in the cycle is chosen. Another alternative is to rely on ELABORATE_BODY to solve most of the problems, and leave ELABORATE as is in Ada 83. A third alternative is to go to a static access-before-elaboration checking approach, so that PROGRAM_ERRORs due to ABEs will never occur, and the programmer need not insert pragma ELABORATEs explicitly to prevent them. b) The work-around involves determining the desired order of elaboration, and moving the pragma ELABORATEs to constrain the ordering without creating an indirect circularity. c) The rearrangement of the pragma ELABORATEs will be legal for Ada 83, but may be underconstraining depending on the linker implementation. d), e) N/A 21) MS-12.1;4.0: Assume the worst when checking a generic body -- Upward Consistent a) Not including the issue of unconstrained actual types (see next item), we estimate that extremely few programs currently take advantage of the other Ada 83 contract model violations. In Ada 9X, the "assume the worst" model for generic bodies require the following: i) a generic formal scalar object of mode IN OUT must be assumed to be of a non-static subtype, and hence requires an others clause when used as a case expression. ii) an array-type formal parameter of a generic formal subprogram must be assumed to be unconstrained, precluding the use of an others choice in an array aggregate used as an actual parameter. In Ada 83, these cases are still under ARG debate (see AI-847 and AI-878). b) The work-around is straightforward: Add an others clause to the case statement, and use a subtype-qualified aggregate to the call. Note this implies that the formal subtype is the same as the actual subtype. If not, one may have to pass the bounds for the actual subtype as separate generic parameters. c) The work-arounds work identically in Ada 83 and Ada 9x. d), e) N/A 22) MS-12.1.2;4.0: "(<>)" required if actual type is unconstrained composite without defaults -- Upward Consistent a) A modest number of generic units are designed to handle actual type parameters that are unconstrained composite types without defaults. In Ada 83, such generics must never declare variables or components of the formal type. b) The work-around is straightforward: If the generic unit can handle unconstrained composite types without defaults as the actual type for a given formal private parameter, add "(<>)" to the specification of that formal private type. c) The work-around is not legal Ada 83 syntax. It must be added in when moving code to an Ada 9X compiler. A structured Ada 83 comment could be recognized by a tool that could perform the transformation in either direction automatically. d), e) N/A 23) MS-12.2;4.0: Exceptions declared in generic bodies need not be replicated for each instantiation -- Upward inconsistent a) We estimate that there are no existing programs (other than ACVCs) that will show upward inconsistencies due to this change. As explained in the section-by-section rationale, a program can only notice this change if it has a generic body that depends on one of its instances, and the instance propagates the exception outside of its scope, and the generic body calls a subprogram of the instance and tries to catch the local exception. b) The work-around is straightforward (though as stated above, we estimate that there are no programs that will need it): Move the declaration into the private part of the generic spec (presuming it is a generic package) or reduce the coverage of the exception handler for the local exception so that it doesn't include the call on the offending instance subprogram. c) The work-around will work identically on Ada 83 and Ada 9x. d) Any potential inconsistencies are warnable. e) The effect of the inconsistency is very mild, namely that an exception propagated by the subprogram of the instance is caught by the handler in the generic body. 24) MS-13.7;4.0 and elsewhere: New identifiers added to language-defined packages -- Upward consistent a) We anticipate that very few programs will collide with the new identifiers added to the various language-defined packages (e.g. SYSTEM, SEQUENTIAL_IO, TEXT_IO, etc.). The collisions will be noticable when the language-defined package is "use"d along with some user-defined package. The rules of use-visibility ensure that these changes will be upward consistent. Note that we could minimize the upward incompatibility by moving some of these new declarations into child packages of the language-defined packages. For example, we could put the declarations of STORAGE_OFFSET, STORAGE_ELEMENT, and STORAGE_ARRAY into a new child of SYSTEM, such as SYSTEM.ADDRESS_OPERATIONS. b) The work-around is straightforward: Change the name of the user-defined entity, or qualify the references to the identifier with a package prefix or a type name. c) The work-arounds will work identically in Ada 83 and Ada 9x. d), e) N/A 25) MS-14.2.2;4.0 and MS-14.3.1;4.0: APPEND_FILE added to FILE_MODE enumeration -- Upward Inconsistent a) We anticipate that a modest number of programs will be made illegal by this change, and an extremely small number will remain legal but produce inconsistent results. Programs with case statements or arrays indexed by FILE_MODE will likely become illegal, unless they use an others choice. Program that depend on the position number of FILE_MODE'LAST, while remaining legal, can show inconsistencies. b) The work-around is straightforward: Add an others or APPEND_FILE choice to array aggregates and case statements with an appropriate value or alternative for appending. c) It is possible to write work-arounds that will work identically in Ada 83 and Ada 9X. d) All inconsistencies are warnable. e) The effect of the inconsistency is presumably mild, as it relates only to the value of FILE_MODE'LAST. ============================== As we have seen, all potential upward inconsistencies are warnable, though in some cases, upward consistent uses may also be flagged. All of the upward inconsistencies we estimate to correspond to rare situations, except perhaps things relating to CHARACTER. The "major" cases of potential upward incompatibility (i.e., will occur in a modest number of programs) we estimate to be: 1) Additional reserved words 2) Changes to CHARACTER 3) Fixing the contract model w.r.t. unconstrained composite actual types (i.e. the "(<>)" syntax) 4) APPEND_FILE addition In all of these cases, the work-arounds are relatively straightforward, and we estimate that the benefits of the change outweigh any potential inconvenience. =============================== ASIDE on NUMERIC_ERROR: By the way, we make no mention of NUMERIC_ERROR above. It seems best to eliminate NUMERIC_ERROR from STANDARD in Ada 9X. Other alternatives (like making it a rename of CONSTRAINT_ERROR) will probably break just as many programs, and leave a "wart" of complexity in the language for no obvious reason. In any case, if we do decide to remove NUMERIC_ERROR it will be upward "inconsistent" to do so (see below for definition), but in a particularly convenient way for transition: package MY_EXCEPTIONS is NUMERIC_ERROR : exception; -- Only use-visible in Ada 9X end MY_EXCEPTIONS with MY_EXCEPTIONS; use MY_EXCEPTIONS; with Text_IO; procedure Main is begin . . . exception when NUMERIC_ERROR | CONSTRAINT_ERROR => -- This would refer to MY_EXCEPTIONS.NUMERIC_ERROR in Ada 9X, -- and STANDARD.NUMERIC_ERROR in Ada 83, if we were to -- eliminate NUMERIC_ERROR from STANDARD. TEXT_IO.Put_Line("Overflow occurred"); end Main; By creating such a package, one can survive the removal of NUMERIC_ERROR without further source changes. Of course if your Ada 83 compiler's RTS already has been switched to always raise CONSTRAINT_ERROR instead of NUMERIC_ERROR, then one could simply delete all references to NUMERIC_ERROR and be done with it. End of ASIDE