4.2.1 User-Defined Literals
{
AI12-0249-1}
Using one or more of the aspects defined below,
a type may be specified to allow the use of one or more kinds of literals
as values of the type.
Static Semantics
{
AI12-0249-1}
{
AI12-0342-1}
The following type-related operational aspects
(collectively known as user-defined literal aspects)
may be specified for a type T:
This aspect is specified by a function_name
that statically denotes a function with a result type of T and
one in parameter that is of type String and is not explicitly
aliased.
Aspect Description
for Integer_Literal: Defines
a function to implement user-defined integer literals.
This aspect is specified by a function_name
that statically denotes a function with a result type of T and
one in parameter that is of type String and is not explicitly
aliased, and optionally a second function [(overloading the first) ]with
a result type of T and two in parameters of type String
that are not explicitly aliased.
Aspect Description
for Real_Literal: Defines
a function or functions to implement user-defined real literals.
This aspect is specified by a function_name
that statically denotes a function with a result type of T and
one in parameter that is of type Wide_Wide_String and is not explicitly
aliased.
Aspect Description
for String_Literal: Defines
a function to implement user-defined string literals.
Ramification:
{
AI12-0342-1}
The following example is legal because the resolution
of an aspect_definition
for an aspect that is defined to be a subprogram is based on the profile
required for that aspect (see 13.1.1):
package Pkg1 is
type T is record X, Y : Integer; end record
with Integer_Literal => Int_Lit;
function Int_Lit (X, Y : T) return Duration; -- Wrong profile.
function Int_Lit (Lit_Image : String) return T; -- Right profile.
end Pkg1;
Discussion:
This means that in this example
package Pkg2 is
type T1 is record
X, Y : Integer;
end record with Integer_Literal => I_L;
function I_L (S : String) return T1 is ((0, 0));
type T2 is new T1;
function I_L (S : String) return T2 is ((1, 1));
X : T2 := 123;
end Pkg2;
{
AI12-0342-1}
{
AI12-0427-1}
When a numeric literal is interpreted as a value
of a non-numeric type T or a string_literal
is interpreted as a value of a type T that is not a string type
(see 4.2), it is equivalent to a call to the
subprogram denoted by the corresponding aspect of T: the Integer_Literal
aspect for an integer literal, the Real_Literal aspect for a real literal,
and the String_Literal aspect for a string_literal.
The actual parameter of this notional call is a string_literal
representing a sequence of characters that is the same as the sequence
of characters in the original numeric literal, or the sequence represented
by the original string literal.
Discussion: This
equivalence defines, for example, the nominal type, the nominal subtype,
and the accessibility level of a user-defined literal. It also has the
consequence that a user-defined literal shall not be of an abstract type
(because that would be equivalent to a nondispatching call to an abstract
function). This equivalence also defines the Dynamic Semantics of evaluating
a user-defined literal.
The (sub)type of the actual
parameter to this call is determined by the profile of the appropriate
aspect, and the bounds of the string_literal
are defined by the usual rules for the bounds of a string_literal.
{
AI12-0342-1}
Such a literal is said to be a user-defined
literal.
{
AI12-0394-1}
When a named number that denotes a value of type
universal_integer is interpreted as a value of a non-numeric type
T, it is equivalent to a call to the function denoted by the Integer_Literal
aspect of T. The actual parameter of this notional call is a String
having a textual representation of a decimal integer literal optionally
preceded by a minus sign, representing the same value as the named number.
{
AI12-0394-1}
When a named number that denotes a value of type
universal_real is interpreted as a value of a non-numeric type
T, it is equivalent to a call to the two-parameter function denoted
by the Real_Literal aspect of T, if any. The actual parameters
of this notional call are each a String with the textual representation
of a decimal integer literal, with the first optionally preceded by a
minus sign, where the first String represents the same value as the numerator,
and the second the same value as the denominator, of the named number
when represented as a rational number in lowest terms, with a positive
denominator.
Legality Rules
{
AI12-0249-1}
{
AI12-0295-1}
{
AI12-0325-1}
{
AI12-0342-1}
The Integer_Literal or Real_Literal aspect shall
not be specified for a type T if the full view of T is
a numeric type. The String_Literal aspect shall not be specified for
a type T if the full view of T is a string type.
{
AI12-0342-1}
For a nonabstract type, the function directly specified
for a user-defined literal aspect shall not be abstract.
{
AI12-0342-1}
For a tagged type with a partial view, a user-defined
literal aspect shall not be directly specified on the full type.
{
AI12-0342-1}
{
AI12-0394-1}
If a nonabstract tagged type inherits any user-defined
literal aspect, then each inherited aspect shall be directly specified
as a nonabstract function for the type unless the inherited aspect denotes
a nonabstract function, or functions, and the type is a null extension.
{
AI12-0394-1}
If a named number that denotes a value of type
universal_integer is interpreted as a value of a non-numeric type
T, T shall have an Integer_Literal aspect. If a named number
that denotes a value of type universal_real is interpreted as
a value of a non-numeric type T, T shall have a Real_Literal
aspect, and the aspect shall denote a function that has two in
parameters, both of type String, with result of type T.
{
AI12-0249-1}
{
AI12-0342-1}
In addition to the places where Legality Rules
normally apply (see 12.3), these rules also
apply in the private part of an instance of a generic unit.
Bounded (Run-Time) Errors
{
AI12-0249-1}
{
AI12-0325-1}
{
AI12-0342-1}
{
AI12-0394-1}
It is a bounded error if the
evaluation of a literal or named number that has an expected type with
a specified user-defined literal aspect propagates an exception. Either
Program_Error or the exception propagated by the evaluation is raised
at the point of use of the value of the literal or named number. If it
is recognized prior to run time that evaluation of such a literal or
named number will inevitably (if executed) result in such a bounded error,
then this may be reported as an error prior to run time.
Implementation Note:
{
AI12-0249-1}
As always, an implementation may apply "as-if"
optimizations (those that result in the same external effects in the
absence of erroneous execution) to the function calls associated with
user-defined literals. In particular, if the function associated with
a user-defined literal aspect has a Global aspect that indicates no references
to global variables, then a number of optimizations are available to
the implementation:
The implementation can
evaluate a user-defined literal function at compile-time if it has access
to the body of the function (for example, if it is inlined), and that
body doesn't reference anything evaluated at runtime. If the compile-time
evaluation results in an exception, this bounded error allows the compilation
unit to be rejected.
Implementations can
use existing permissions (see 6.1.2) to avoid
evaluating the function associated with a user-defined literal more than
once for a particular literal value. This evaluation can be "hoisted"
(done once per compilation unit during the elaboration of the unit) if
the compiler can prove that the function doesn't depend on any constants
or locals with a runtime value not yet elaborated.
If the literal value
is not needed by the execution of the program, the function call can
be omitted even if it might have side-effects (again, see 6.1.2).
Examples
{
AI12-0429-1}
Examples of the specification and use of user-defined
literals:
{
AI12-0312-1}
subtype Roman_Character is Wide_Wide_Character
with Static_Predicate =>
Roman_Character in 'I' | 'V' | 'X' | 'L' | 'C' | 'D' | 'M';
{
AI12-0312-1}
Max_Roman_Number : constant := 3_999; -- MMMCMXCIX
{
AI12-0312-1}
type Roman_Number is range 1 .. Max_Roman_Number
with String_Literal => To_Roman_Number;
{
AI12-0312-1}
function To_Roman_Number (S : Wide_Wide_String) return Roman_Number
with Pre => S'Length > 0 and then
(for all Char of S => Char in Roman_Character);
{
AI12-0312-1}
{
AI12-0386-1}
function To_Roman_Number (S : Wide_Wide_String) return Roman_Number is
(declare
R : constant array (Integer range <>) of Roman_Number :=
(for D in S'Range => Roman_Digit'Enum_Rep
(Roman_Digit'Wide_Wide_Value (''' & S(D) & ''')));
-- See 3.5.2 and 13.4
begin
[for I in R'Range =>
(if I < R'Last and then R(I) < R(I + 1) then -1 else 1) * R(I)]
'Reduce("+", 0)
);
{
AI12-0312-1}
X : Roman_Number := "III" * "IV" * "XII"; -- 144 (that is, CXLIV)
Extensions to Ada 2012
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe