!standard 04.05.05 (17) 00-01-25 AI95-00152/06 !class confirmation 96-09-04 !status Response 2000 00-01-25 !status WG9 approved 98-06-12 !status ARG Approved 11-0-0 98-04-01 !status ARG Approved (with changes) 12-0-1 97-11-16 !status work item 96-09-08 !status received 96-09-04 !priority Medium !difficulty Medium !qualifier Clarification !subject Operators not inherited from root numeric types !summary Predefined operators, including those of the root numeric types, are not inherited. Instead, types have predefined operators by specific language rules. !question Consider the operators described in 4.5.5(17), such as function "*" (Left: root_real; Right: root_integer) return root_real; It would seem logical to assume that root_real is declared immediately within the visible part of Standard, which means that this operator is a primitive subprogram of type root_real. By 3.5.6(3), Float is derived from root_real. Does Float therefore inherit this operator? (No.) If so, the following function exists: function "*" (Left: Float; Right: root_integer) return Float; which would make the following legal: declare X: Float; begin ... X := X * 2; -- Legal? (No.) end; The same applies to a user-defined numeric type. !response Although floating point types are derived from root_real, this does not imply any inheritance of subprograms. Inheritance happens for a derived type that is declared by a derived_type_definition, by 3.4(7). For private extensions, 7.3(16) and 12.5.1(20) apply. No such rules apply to other kinds of type_definition; therefore, no inheritance takes place for such types. Section 4 explicitly defines the predefined operators that are implicitly declared for a type, according to its class. The mechanism of inheritance is not used for this purpose. Note that this is not the only way in which the implicit derivation from a root numeric type is different from derivation via an explicit derived_type_definition. See, for example, 3.5.4(14-14.a). !ACATS test It would be possible to create a B-Test to insure that the mixed operators are not inherited, but it would have little value. !appendix !section 4.5.5(17) !subject Operators inherited from root_real multiplying operators !reference RM95-4.5.5(17) !from Norman Cohen !reference 96-5609.a Norman H. Cohen 96-6-21>> !discussion Consider the operators described in 4.5.5(17), such as function "*" (Left: root_real; Right: root_integer) return root_real; By 3.5.6(3), Float is derived from root_real, so we would expect it to inherit function "*" (Left: Float; Right: root_integer) return Float; which would have the surprising effect of making the following legal: declare X: Float; begin ... X := X * 2; end; By 3.5.4(14), Integer is derived from root_integer, so we would expect it to inherit function "*" (Left: root_real; Right: Integer) return root_real; which would have the somewhat less surprising--and arguably useful--effect of allowing Largest_Integer_Value : constant := 1.0 * Integer'Last; type Fixed_Point_Type_With_Range_Of_Integer is delta 1.0 range 1.0*Integer'First .. 1.0*Integer'Last; However, the effect of these declarations can be achieved by wrapping Integer'First and Integer'Last inside Integer'Pos(...), or by declaring universal_integer named numbers: Integer_First : constant := Integer'First; Integer_Last : constant := Integer'Last; Here is a line of reasoning that would allow us to deduce that ordinary numeric types do not inherit from the operators in 4.5.5(17): Although the operators are declared in Standard (A.1(29,30,31)), no declarations for root_integer and root_real appear in Standard, only comments in A.1(11) and and A.1(20) stating that these types "are predefined". Thus the multiplying operators are not primitive operations of the root numeric types. While the MRT may have had this in mind all along, I would feel more comfortable if it were explicitly stated in an AI. **************************************************************** !section 4.5.5(17) !subject Operators inherited from root_real multiplying operators !reference RM95-4.5.5(17) !reference 96-5609.a Norman H. Cohen 96-6-21 !from Bob Duff !reference 96-5611.a Robert A Duff 96-6-22>> !discussion > Consider the operators described in 4.5.5(17), such as > > function "*" (Left: root_real; Right: root_integer) return root_real; > > By 3.5.6(3), Float is derived from root_real, so we would expect it to > inherit > > function "*" (Left: Float; Right: root_integer) return Float; This is the hole in the argument. There are no rules saying that all types inherit things from their parent/ancestor. Instead, it's all based on syntax rules. 3.4 says that derived types declared by a derived_type_definition inherit certain things. (Note the use of "the derived type", as opposed to "a derived type", in 3.4(7).) 7.3(16) and 12.5.1(20) say that more-or-less the same thing happens for [formal] private extensions. But there is no such rule for all types that are derived from some parent, and in particular, there is no such rule for Float. Float is declared by a floating_point_whatever_it_is, and not by a derived_type_definition. > which would have the surprising effect of making the following legal: > > declare > X: Float; > begin > ... > X := X * 2; Illegal. Whether that's surprising or not, I suppose, depends on your point of view. ;-) > end; > > By 3.5.4(14), Integer is derived from root_integer, so we would expect > it to inherit > > function "*" (Left: root_real; Right: Integer) return root_real; No, Integer is not declared by a derived_type_definition, so it doesn't inherit anything. > Here is a line of reasoning that would allow us to deduce that ordinary > numeric types do not inherit from the operators in 4.5.5(17): Although > the operators are declared in Standard (A.1(29,30,31)), no declarations > for root_integer and root_real appear in Standard, only comments in > A.1(11) and and A.1(20) stating that these types "are predefined". Thus > the multiplying operators are not primitive operations of the root > numeric types. I think root_integer and root_real *are* declared in Standard. Luckily, there's no need for this line of reasoning. > While the MRT may have had this in mind all along, I would feel more > comfortable if it were explicitly stated in an AI. What the MRT had in mind was the syntax-based definition of what gets inherited, as explained above, which does not apply to all types that happen to be defined to be derived magically from some parent. I seem to remember some earlier versions of Ada 9X that tried to fold all the predefined operators into the notion of inheritance, but it didn't work, and it was uncomfortably different from the Ada 83 definition, so we gave up on that idea. Note also that base ranges wouldn't work right, if "type T is range 1..10;" were defined to be equivalent to "type T is new range 1..10;". Luckily, it's not. - Bob P.S. This seems to be a case where Pascal Leroy (whom I cc'ed) would say, "We don't need a whole 'nother AI for this". On the other hand, Norman explicitly asked for an official AI ruling. **************************************************************** !section 4.5.5(17) !subject Operators inherited from root_real multiplying operators !reference RM95-4.5.5(17) !reference 96-5609.a Norman H. Cohen 96-6-21 !reference 96-5611.a Robert A Duff 96-6-22 !from Norman Cohen !reference 96-5612.a Norman H. Cohen 96-6-24>> !discussion > There are no rules saying that all > types inherit things from their parent/ancestor. Instead, it's all > based on syntax rules. 3.4 says that derived types declared by a > derived_type_definition inherit certain things. (Note the use of "the > derived type", as opposed to "a derived type", in 3.4(7).) Oh, give me a break! ;-) This is obscurantism at its worst. What is the point of even saying that the numeric types are "implicitly derived from" the root types if this implies nothing about inheritance of operations? And where is there any suggestion in the RM that types can be derived in any way other than by derived-type declarations (which would be necessary to deduce that "implictly derived from" can mean anything other than "declared by one or more implicit derived-type declarations")? > I think root_integer and root_real *are* declared in Standard. Do you mean you think you see declarations in A.1 of the form type root_integer is ...; type root_real is ...; or just that that is your preferred interpretation of the statement in A.1 that such types exist? I certainly prefer my explanation for why the 4.5.5(17) operations are not inherited by Integer and Float. > P.S. This seems to be a case where Pascal Leroy (whom I cc'ed) would > say, "We don't need a whole 'nother AI for this". On the other hand, > Norman explicitly asked for an official AI ruling. This is not an obvious confirmation, nor is it a purely theoretical musing. I had stumbled into it by writing something like delta 1.0 range -1.0*Integer'First .. 1.0*Integer'Last For a while, I had convinced both myself and some GNAT implementors that this should be legal, and we were all relieved to come up with a line of reasoning arguing that it is not. I find it quite plausible that others will stumble upon it and be equally successful in bamboozling implementors into believing that the operators are inherited, so it seems worthwhile to document the intent of the standard. Note that we are agreed about the desired conclusion, and the only controversy is about which legalisms we should use to reach that conclusion. **************************************************************** !section 4.5.5(17) !subject Operators inherited from root_real multiplying operators !reference RM95-4.5.5(17) !reference 96-5609.a Norman H. Cohen 96-6-21 !reference 96-5611.a Robert A Duff 96-6-22 !reference 96-5612.a Norman Cohen !from Bob Duff !reference 96-5613.a Robert A Duff 96-6-24>> !discussion > Oh, give me a break! ;-) > > This is obscurantism at its worst. A fair criticism, perhaps, but it doesn't change the facts. ;-) I happen to think that it is ludicrous that (in Ada 83 and Ada 95), "type T is range 1..10;" is called a type declaration, but it does *not* declare a type called T. But just because I don't like it, doesn't mean it's false (in our little RM universe). >... What is the point of even saying that > the numeric types are "implicitly derived from" the root types if this > implies nothing about inheritance of operations? The point is so that the basic type model and type matching rules will work out nicely. We want to have a class, "the class of all integer types", rooted at root_integer, with a corresponding universal type universal_integer. (Note that universal_integer is sort of like root_integer'Class, as explained in 3.4.1(6).) This model then fits in with the type matching rules of 8.6, plus the definitions of "derived from", "cover", and "descendant" in 3.4.1. The numeric classes are somewhat special, because we want to talk about the root of the classes and corresponding universal types explicitly. There was no need for, e.g., a root_access or universal_access type, so access types don't bother with this stuff. >... And where is there any > suggestion in the RM that types can be derived in any way other than by > derived-type declarations (which would be necessary to deduce that > "implictly derived from" can mean anything other than "declared by one or > more implicit derived-type declarations")? There's a "hint" in RM-3.5.4(14), and more hints in the AARM annotations following that: 14 {root_integer} {Min_Int} {Max_Int} A type defined by an integer_type_ definition is implicitly derived from root_integer, an anonymous predefined (specific) integer type, whose base range is System.Min_Int .. System.Max_ Int. However, the base range of the new type is not inherited from root_ integer, but is instead determined by the range or modulus specified by the integer_type_definition. {universal_integer [partial]} {integer literals} [Integer literals are all of the type universal_integer, the universal type (see 3.4.1) for the class rooted at root_integer, allowing their use with the operations of any integer type.] 14.a Discussion: This implicit derivation is not considered exactly equivalent to explicit derivation via a derived_type_ definition. In particular, integer types defined via a derived_type_ definition inherit their base range from their parent type. A type defined by an integer_type_definition does not necessarily inherit its base range from root_integer. It is not specified whether the implicit derivation from root_integer is direct or indirect, not that it really matters. All we want is for all integer types to be descendants of root_integer. The RM clearly says that this sort of derivation is different from the explicit kind, in that the base range is not inherited. The AARM elaborates on that point, making it quite clear that this implicit derivation is somewhat magical. If you don't *like* that fact, I have some sympathy, but it's true nonetheless. > > I think root_integer and root_real *are* declared in Standard. > > Do you mean you think you see declarations in A.1 of the form > > type root_integer is ...; > type root_real is ...; > > or just that that is your preferred interpretation of the statement in > A.1 that such types exist? I can't think of any other reasonable interpretation of A.1(11): 4 package Standard is ... 11 -- The integer type root_integer is predefined. -- The corresponding universal type is universal_integer. Why would this be mentioned in package Standard if the types aren't declared there? If you don't buy that, then how about 3.2.1(10): 10 {predefined type} The predefined types [(for example the types Boolean, Wide_Character, Integer, root_integer, and universal_integer)] are the types that are defined in [a predefined library package called] Standard[; this package also includes the [(implicit)] declarations of their predefined operators]. [The package Standard is described in A.1.] 10.a Ramification: We use the term ``predefined'' to refer to entities declared in the visible part of Standard, to implicitly declared operators of a type whose semantics are defined by the language, to Standard itself, and to the ``predefined environment''. We do not use this term to refer to library packages other than Standard. For example Text_IO is a language-defined package, not a predefined package, and Text_IO.Put_Line is not a predefined operation. > I certainly prefer my explanation for why the 4.5.5(17) operations are > not inherited by Integer and Float. As explained above, I don't think it works. Now, if root_integer is declared in Standard, and if you go with the idea that implicit derivation works just like explicit derivation, then we have to wonder whether the operator you mentioned is user-defined or predefined, since only user-defined operators get inherited. I think "predefined" makes the most sense, but I can't find any compelling justification for that in the RM wording. I would prefer not to have to answer that question. > This is not an obvious confirmation, To me, it seems like an obvious confirmation (though I don't blame you for being surprised by it). To you and Pascal, it is apparently quite non-obvious. That's why I'm a bit nervous about having been handed the responsibility of deciding what's "obvious" -- it's too easy for me to "cheat" by knowing what I meant to write. (Actually, I think Tucker wrote 3.5.4(14,14.a), but still.... I do remember discussing the wording of that particular paragraph explicitly with Tucker.) I'm inclined to say that if Norman Cohen demands an official AI ruling, then he should get one. I hope all ARG members will review my decisions as to which issues should be seriously and promptly considered by the ARG. >...nor is it a purely theoretical > musing. I had stumbled into it by writing something like > > delta 1.0 range -1.0*Integer'First .. 1.0*Integer'Last > > For a while, I had convinced both myself and some GNAT implementors that > this should be legal, and we were all relieved to come up with a line of > reasoning arguing that it is not. I find it quite plausible that others > will stumble upon it and be equally successful in bamboozling > implementors into believing that the operators are inherited, so it > seems worthwhile to document the intent of the standard. OK. > Note that we are agreed about the desired conclusion, and the only > controversy is about which legalisms we should use to reach that > conclusion. Right. It's nice when two parties can agree on what they're arguing about, even if they don't agree on the right answer! - Bob P.S. I talked about root_integer above. I didn't look up root_real and root_fixed, but I assume their wording is similar. ****************************************************************