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 any 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 explictly 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 explictly aliased.
Aspect Description for Real_Literal:
Defines a function 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 explictly
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 preceding rules are Name Resolution
Rules (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;
{
AI12-0342-1}
User-defined literal aspects are inherited according to the rules given
in
13.1.
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}
the initial value of Pkg.X is (0,0), not (1,1).
{
AI12-0342-1}
When a numeric literal is interpreted as a value of a non-numeric type
T or a
string_literal
is interpreted 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
having the textual representation of the original (numeric or 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.
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}
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 and the type is a null extension.
{
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}
It is a bounded error if the evaluation of a literal
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.
If it is recognized prior to run time that evaluation of such a literal
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-0312-1}
subtype Roman_Character
is 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 : String)
return Roman_Number
with Pre => S'Length > 0
and then
(
for all Char
of S => Char
in Roman_Character);
{
AI12-0312-1}
function To_Roman_Number (S : String)
return Roman_Number
is
(
declare
R :
constant array (Integer
range <>)
of Integer :=
(
for D
in S'Range => Roman_Digit'Enum_Rep
(Roman_Digit'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