Version 1.2 of acs/ac-00090.txt

Unformatted version of acs/ac-00090.txt version 1.2
Other versions for file acs/ac-00090.txt

!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

From: Alexandre E. Kopilovitch
Sent: Friday, November 21, 2003  2:35 PM


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.


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.


The proposal is based on two new related attributes: With_Literals and

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.


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]

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.


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;


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(<literal>);

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.


Questions? Ask the ACAA Technical Agent