!standard 7.03.01(00) 03-12-05 AC95-00090/01 !class amendment 03-12-05 !status received no action 03-12-05 !status received 03-11-21 !subject Literals for private types !appendix From: Alexandre E. Kopilovitch Sent: Friday, November 21, 2003 2:35 PM !summary Here is proposed a way for introducing implicitly-convertible literals for general types, including private types, and in particular, for various string types, such as Unbounded_String. !problem There is a well-known issue of explicit conversions needed for assigning string literals to Unbounded_Strings (or Bounded_Strings). Although a minor and insignificant detail for many regular Ada users, it is annoying for the unaccustomed eye (and often is treated as a symptom of unfriendliness of the language), and repeatedly invokes public discussions even among programmers quite familiar with Ada. The commonly used renaming of the conversions to "+" does not close the issue, but just eases it slightly. !proposal The proposal is based on two new related attributes: With_Literals and Literal_Conversion. Attribute With_Literals relates to a formal parameter of a user-defined subprogram, and requests implicit conversions of literal actual arguments to the type's values for the parameter. Attribute Literal_Conversion relates to a user-defined type, and provides a family of primitive conversion functions for literals for the type - which should be used for With_Literals requests as well as for language-defined operations, e.g. assignment. Syntax Attribute With_Literals has no arguments. It may be used in parameter specifications in subprogram declarations only: type T is ...; procedure P (Arg : in T'With_Literals); so the syntax of parameter specification (ARM 6.1.15) will include additional case: parameter specification::= defining_identifier_list:mode subtype_mark[:=default_expression] |defining_identifier_list:mode subtype_mark'With_Literals[:=default_expression] |defining_identifier_list:access_definition[:=default_expression] Attribute With_Literals also may be used in a class-wide parameter; in this case it must follow the Class attribute: procedure P (Arg : in T'Class'With_Literals); procedure Q (Arg : in T'With_Literals'Class); -- illegal Attribute Literal_Conversion has no arguments. It is always applied to a type. It may be defined for a type using either the principal form for T'Literal_Conversion use F; or the "null" form: for T'Literal_Conversion use null; where T is a type and F is a common (overloaded) name for a set of functions, which have one String or numeric parameter and return T. Legality Rules Attribute With_Literals may be used with mode IN only. It may be applied either to a type for which the attribute Literal_Conversion is defined or to a formal type parameter in a generic. In the latter case the actual type at instantiation of that generic must either be a type for which literals are defined by the language (that is, String or one of numeric types), or the Literal_Conversion attribute must be defined for that type. Attribute Literal_Conversion may be defined for a user-defined type within specs of the package where the type is declared. using either the principal form or the "null" form. It may be also defined or redefined in private part of the specs of any other package (where the type is visible), using "null" form only. For any type and any package in which this type is visible, attribute Literal_Conversion for this type may be defined (or redefined) in this package's specs at most once. Static Semantics Attribute With_Literals requests implicit conversion of the actual argument for the corresponding parameter in a call of the subprogram if the actual argument is a literal and Literal_Conversion attribute is defined and visible in the current context and provides a suitable conversion function. If Literal_Conversion is defined and visible, but does not provide a suitable conversion function then the With_Literals attribute is ignored (silently) for this actual argument. All predefined (language-defined) operations involving a type for which the Literal_Conversion attribute is defined along with the type's declaration are considered as having an implicit With_Literals attribute in all their IN mode parameters that belong to that type. Attribute Literal_Conversion represents either a set of all primitive functions of the type (including private ones) that have the indicated name, a single String or numeric parameter and return the type (if defined using the principal form of the definition) or an empty set (if defined using "null" form of the definition). A derived type inherits the Literal_Conversion attribute definition from its parent type. A derived type may override this inherited definition by its own definition in the package specs where the derived type is declared. For any user-defined type, any package where this type is visible may define or redefine the Literal_Conversion attribute for itself and all its child packages by using the "null" definition form within the private part of its specs (thus providing an empty set of conversion functions for the package and all its child packages). However, this "null" (re)definition is not inherited by derived types, which may be declared in this package or its child packages, and the original definition is inherited instead. !example For equipping the Unbounded_String type with implicitly-convertible string literals, the following single statement should be added to the public part of the specs of Ada.Strings.Unbounded package : for Unbounded_String'Literal_Conversion use To_Unbounded_String; With that an assignment of string literal to Unbounded_String becomes possible (without explicit conversions), but still nothing more then that, and no additional potential for ambiguity emerges. Then, if we wish to allow implicit conversion from string literals for our user-defined function Compare, we use With_Literal attributes for its parameters: function Compare (Source : in Unbounded_String'With_Literals; Target : in Unbounded_String'With_Literals) return Boolean; !discussion With this proposal we get literals for private types without increasing potential for ambiguity. Full compatibility with existing code is guaranteed for the case when the proposed features are included in the language, but not actually used - just because with this proposal every change in behaviour must be requested explicitly by use of new attributes. So, defining the Literal_Conversion attribute for Unbounded_String (as in the example above) does not produce any harm, but makes possible assignment of string literals to Unbounded_Strings without explicit conversions. All other intended uses do not seem inherently dangerous, because they are tightly controlled by the With_Literals attribute. Explanations for some particular decisions A particular form (from two possible) for combining the With_Literals attribute with the Class attribute was chosen: procedure P (Arg : in T'Class'With_Literals); procedure Q (Arg : in T'With_Literals'Class); -- illegal just because with this rule there is no need for any change in the definition of existing Class attribute. A "null" definition for the Literal_Conversion attribute is useful for two purposes: first, it provides an opportunity to use With_Literals attribute without actually providing conversion functions; second, it makes possible an instantiation of a generic that uses With_Literals attribute for a formal type parameter with an argument type for which the Literal_Conversion attribute is not defined in the package where the type is declared. Only a "null" redefinition is permitted for the Literal_Conversion attribute for two reasons: first, because it is desirable to allow inheritance of this attribute for child packages as well as for the derived types, and a possible collision of these two hierarchies (for a derived type declared in a child package of a package that redefined the attribute) becomes more confusing if both are allowed to provide non-null candidates; second, while I see a possible need for nullifying the Literal_Conversion attribute, I don't see a need for redefining it to another family of functions - I think that such redefinition will have more potential for confusion than help. I think that if a programmer really needs a "non-standard" conversion for literals then he should use explicit conversions, and the proposed mechanism for implicit conversion is not good for that case. Possible variations There is an alternative treatment of redefinition ("nullifying") of the Literal_Conversion attribute for predefined operations. We may state that with a "nullified" Literal_Conversion attribute, only explicit With_Literals attributes will be ignored, that is, only user-defined subrograms will lose the opportunity to use literals for actual arguments for the type, but assignments of literals to variables of this type (with implicit conversion) will still be possible (they will use the initial definition of the Literal_Conversion attribute, provided along with the type declaration). We may also include an opportunity to limit the overriding of the Literal_Conversion attribute for derived types. For that we may provide the class-wide form for T'Class'Literal_Conversion use F; and class-wide "null" form for T'Class'Literal_Conversion use null; stating in the Legality Rules that definitions that use these forms can be overriden by the second of them (class-wide "null" form) only. Also, we can add an argument to the Literal_Conversion attribute, making it specific (and separate) for each possible literal type (that is, String or numeric type). With this alternative a separate definition should be made for each literal type for which an implicit conversion is desired: for T'Literal_Conversion(String) use F_1; for T'Literal_Conversion(Integer) use F_2; -- F_1 also is legal here and the corresponding "null" forms may be used separately: for T'Literal_Conversion(String) use null; both for definitions and redefinitions of the Literal_Conversion attribute. Within this alternative it is natural to require in Legality Rules that a suitable primitive function with indicated name must be present for the type. Finally, we can allow explicit use of the Literal_Conversion attribute as a function provider in a call: ... := T'Literal_Conversion(); stating in the Legality Rules that for this case the Literal_Conversion attribute must be defined and visible in the call's context, and it must provide a suitable function. ****************************************************************