the syntax of const-expr does not allow for the construction of error values.
The result of a const-expr
is always immutable.
simple-const-expr := nil-literal | boolean-literal | [Sign] int-literal | [Sign] floating-point-literal | string-literal | constant-reference-expr
A simple-const-expr is a restricted form of const-expr used in contexts where various forms of constructor expression would not make sense. Its semantics are the same as a const-expr.
literal := nil-literal | boolean-literal | numeric-literal | string-literal | byte-array-literal numeric-literal := int-literal | floating-point-literal
A numeric-literal represents a value belonging to one of the basic types int, float or decimal. The basic type to which the value belongs is determined in three stages.
FloatTypeSuffix
or is a
HexFloatingPointLiteral
, then the the only candidate is float;DecimalTypeSuffix
, then the
only candidate is decimal;floating-point-literal
,
the candidates are float and decimal;The precise type of a numeric-literal is the singleton type containing just the shape of the value that the numeric-literal represents. The broad type is the basic type of which the precise type is a subset.
byte-array-literal := Base16Literal | Base64Literal Base16Literal :=base16
WS`
HexGroup* WS`
HexGroup := WS HexDigit WS HexDigit Base64Literal :=base64
WS`
Base64Group* [PaddedBase64Group] WS`
Base64Group := WS Base64Char WS Base64Char WS Base64Char WS Base64Char PaddedBase64Group := WS Base64Char WS Base64Char WS Base64Char WS PaddingChar | WS Base64Char WS Base64Char WS PaddingChar WS PaddingChar Base64Char :=A
..Z
|a
..z
|0
..9
|+
|/
PaddingChar :==
WS := WhiteSpaceChar*
The static type of byte-array-literal is byte[N]
, where N is the
number of bytes encoded by the Base16Literal or Base64Literal. The inherent type
of the array value created is also byte[N]
.
Template expressions make use of strings enclosed in backticks with interpolated expressions.
BacktickString :=`
BacktickItem* Dollar*`
BacktickItem := BacktickSafeChar | BacktickDollarsSafeChar | Dollar* interpolation interpolation :=${
expression}
BacktickSafeChar := ^ (`
|$
) BacktickDollarsSafeChar :=$
+ ^ ({
|`
|$
) Dollar :=$
string-template-expr := string
BacktickString
A string-template-expr
interpolates the results of evaluating
expressions into a literal string. The static type of the expression in each
interpolation must be a subtype of
boolean|int|float|decimal|string
. Within a
BacktickString
, every character that is not part of an
interpolation
is interpreted as a literal character. A
string-template-expr is evaluated by evaluating the expression in each
interpolation in the order in which they occur, and converting the result of the
each evaluation to a string using the ToString abstract
operation with the direct style. The result of evaluating the
string-template-expr
is a string comprising the literal characters
and the results of evaluating and converting the interpolations, in the order in
which they occur in the BacktickString
.
The static type of the result of the string-template-expr is
string
, modified by the usual singleton
typing rules.
A literal `
can be included in string template by using an
interpolation ${"`"}
.
xml-template-expr := xml
BacktickString
An XML template expression constructs an xml value as follows:
content
in the W3C XML
Recommendation. For the purposes of parsing as XML, each interpolated expression
is interpreted as if it were an additional character allowed by the CharData and
AttValue productions but no other. The result of this step is an XML Infoset
consisting of an ordered list of information items such as could occur as the
[children] property of an element information item, except that interpolated
expressions may occur as Character Information Item or in the [normalized value]
of an Attribute Information Item. Interpolated expressions are not allowed in
the value of a namespace attribute.BacktickString
,
and converted to strings if necessary. A new copy is made of the xml value and
the result of the expression evaluations are inserted into the corresponding
position in the newly created xml value. This xml value is the result of the
evaluation. The contextually expected type for an interpolated expression that
occurs within an Attribute Information Item is string, and otherwise is xml;
this ensures that a query expr will work as expected. Note that if the static
type is not a subtype of the contextually expected type, it is not an error:
rather the value will be converted to a string.An xml-template-expr occurring within a const-expr will construct an xml value that has its read-only bit on.
raw-template-expr := BacktickString
A raw-template-expr constructs an object belonging to the abstract RawTemplate object type.
A raw-template-expr is evaluated by
insertions
field with value v
strings
field consisting of a read-only array of strings
containing the characters in BacktickString outside of interpolations, split at
the interpolation points; the length of the this array is one more than the
length of v.The result of the raw-template-expr is the newly constructed object.
For each raw-template-expr there is a separate class, which
is a subtype of the RawTemplate type. The objects constructed by a
raw-template-expr belong to this class. The type of the
insertions
field is determined from the contextually expected type
or the static type of the expressions in the BacktickString in the same way as
with a list-constructor-expr. The strings
and
insertions
fields will be read-only if required by the contextually
expected type. The value of the strings
field is constructed once
by the class. The value of the insertions
is
constructed once for each evaluation of the raw-template-expr.
Each basic type of structure has its own expression syntax for constructing a value of the type.
structural-constructor-expr := list-constructor-expr | table-constructor-expr | mapping-constructor-expr
An structural-constructor-expr occurring within a const-expr will construct a structural value that has its read-only bit on.
list-constructor-expr :=[
[ list-member-list ]]
list-member-list := list-member (,
list-member)* list-member := single-list-member | spread-list-member single-list-member := expression spread-list-member :=...
expression
A list-constructor-expr creates a new list value. The list is constructed by processing each list-member in the order specified as follows. The expression in the list-member is first evaluated resulting in a value v. If the list-member is a single-list-member, then v is added to the list being constructed. Otherwise v must be a list, and every member of v is added in order to the list being constructed. It is a compile-time error if the static type of the expression in a spread-list-member is not a subtype of list.
If there is a contextually expected type, then the inherent type of the newly created list is derived from the applicable contextually expected type. If the applicable contextually expected type is a list type descriptor, then that used as the inherent type. If the applicable contextually expected type is a union type descriptor, then it is a compile-time error unless there is exactly one member of the union that is a list type descriptor and that allows list shapes of a length that the static types of the expressions in the list-member-list make it possible for the constructed list to have; this list type descriptor is used as the inherent type. If there is a spread-list-member, and the type of its expression is not fixed length, then the minimum of the total number of members that may be added to the list being constructed by this spread-list-member and any previous list-member must be greater than or equal to the minimum length required by the inherent type. The static type of the list-constructor-expr will be the same as the inherent type.
If there is no contextually expected type, then the inherent type will be a
tuple-type-descriptor derived from the static type of each expression in the
list-member-list. Each single-list-member that does not have a preceding
spread-list-member contributes a member-type-descriptor to the
tuple-type-descriptor; the type of the member-type-descriptor will be the broad
type of the expression in the single-list-member. If there is a
spread-list-member, then a tuple-rest-descriptor T...
is added
where T is the smallest type such that the broad type of the expression in every
other single-list-member is a subtype of T
, and the type of the
expression in each spread-list-member is a subtype of T[]
.
If the list-constructor-expr does not have a contextually expected type, then
the contextually expected type for the expression in a spread-list-member is
(any|error)[]
, and the expression in a single-list-member does not
have a contextually expected type. Otherwise the contextually expected type for
each list-member is derived from the contextually expected type of the
list-constructor-expr. If there is a spread-list-member that is not the last
list-member in the list-member-list, then the contextually expected type used
from that point on is approximated with a tuple-rest-descriptor.
A member of a list can be filled in automatically if the FillMember abstract operation would succeed on it. The inherent type of a list establishes either a fixed length for the list or just a minimum length for the list, which may be zero. In either case, a list constructor may specify only the first k members, provided that for each i from k + 1 up to the fixed length of the list, the i-th member can be filled in automatically.
mapping-constructor-expr :={
[field (,
field)*]}
field := specific-field | computed-name-field | spread-field specific-field := [readonly
] (field-name | string-literal):
value-expr | [readonly
] variable-name value-expr := expression computed-name-field :=[
field-name-expr]
:
value-expr field-name-expr := expression spread-field :=...
expression
A mapping-constructor-expr creates a new mapping value.
A specific-field specifies a single field, where the field name is known at
compile-time. A specific-field that consists of just a variable name
x
is equivalent to a field x:
x
.
The static type of the expression in a spread-field must allow only mapping
values, i.e. must be a subtype of map<any|error>
. All the
fields of the mapping value that results from evaluating that expression are
included in the mapping value being constructed. It is a compile-time error if
the static type of the expression in a spread-field allows a field that
duplicates a specific-field or that could also occur in another spread-field.
Note that a spread-field with an inclusive record type of record { never
x?; }
cannot duplicate a specific field for x
.
If there is a contextually expected type, then the inherent type of the newly created mapping is derived from the applicable contextually expected type. If the applicable contextually expected type is a mapping type descriptor, then that used as the inherent type. If the applicable contextually expected type is a union type descriptor, then any members of the union that are inconsistent with the field names specified in a specific-field in the mapping-constructor-expr will be ignored; it is a compile-time error if this does not leave a single mapping type descriptor, which is then used as the inherent type. The static type of the mapping-constructor-expr will be the same as the inherent type.
If there is no contextually expected type, then the inherent type will be an
exclusive-record-type-descriptor with an individual-field-descriptor for each
specific-field; the type of each field-descriptor will be the broad type of the
value-expr in the field, unless the field is read-only in which case the type of
the field-descriptor will be the precise type. The static type of the expression
in every spread-field will also be added to the inherent type. If there are
fields specified as a computed-name-field, then there will also be a
record-rest-descriptor T...
, where T
is the union of
the broad types of the value-expr in all such fields.
If a specific-field does not use a string-literal for the name of the field and the inherent type descriptor is a record type descriptor, then the record type descriptor must include an individual-field-descriptor for that field.
If the inherent type descriptor is a record type descriptor, a field will be added to the constructed value using the default value from the type descriptor for any field that is not specified explicitly in the mapping constructor and that has a default value.
If there is a contextually expected type, then the type that the inherent type requires for each field provides the contextually expected type for the value-expr in a field; otherwise there is no contextually expected type for the value-expr for fields. If there is a contextually expected type, the contextually expected type for the expression in a spread-field is map<T>, where the T is the smallest type such that the inherent type is a subtype of map<T>. The contextually expected type for a field-name-expr is string.
A computed-name-field specifies a single field, where the name of the field is specified by an expression enclosed in square brackets. A mapping-constructor-expr first constructs a mapping value without considering any computed-name-field. The effect of a computed-name-field is to modify the member of the mapping with the specified name after the mapping has been constructed. If the modification is incompatible with the inherent type, then the mapping-constructor-expr will panic. The modifications are performed in the order in which the computed-name-fields occur in the mapping-constructor-expr.
When the inherent type is equivalent to a map-type-descriptor, the order in which fields are added to mapping value being constructed (and thus the iteration order of the fields) is the same as the order of the fields in the mapping-constructor-expr, except that all fields specified by a computed-named-field are added after all other fields.
If the applicable contextually expected type is a subtype of readonly, then the
mapping will be constructed with its read-only bit on. If the inherent type
makes a specific field readonly, then that field will be constructed with its
read-only bit on. A specific-field that starts with readonly
will
also be constructed with its read-only bit on.
table-constructor-expr :=table
[key-specifier][
[row-list]]
row-list := mapping-constructor-expr (,
mapping-constructor-expr)*
A table-constructor-expr creates a new table value. The members of the table come from evaluating each mapping-constructor-expr in the row-list in order.
For example,
table key(email) [ { email<EMAIL_ADDRESS>firstName: "Sanjiva", lastName: "Weerawarana" }, { email<EMAIL_ADDRESS>firstName: "James", lastName: "Clark" } ]
The inherent type of the constructed table is a table type descriptor including
a key-specifier table<T> key(ks)
, where
T
is the member type and ks
is
the key sequence. The inherent type is determined from the contextually expected
type together with the table-constructor-expr. The static type of the
table-constructor-expr will be the same as this inherent type.
If there is a contextually expected type, then the member type of inherent type of the newly created table is derived from the applicable contextually expected type, which must be a table-type-descriptor. If there is no contextually expected type, then the member type of the inherent type is derived from the the static type of the expressions for the members: the member type will be the smallest record type that is a supertype of the static types of all the expressions in the row-list. It is an error if there is no contextually expected type and the row-list is empty.
The key sequence of the inherent type comes from the key-specifier of applicable contextually expected type or the key-specifier in the table-constructor-expr. If both of these are present, then they must be the same. If neither of them are present, then the key sequence is empty. The key sequence of the table value is the same as that of its inherent type. The key sequence and member type must meet the same consistency requirements as if they were specified together in a table type descriptor. For every field-name in the key sequence of the inherent type every mapping-constructor-expr in the row-list must include a specific-field that has a value-expr that is a const-expr. It is a compile-time error if two or more rows of the table have the same key value.
If there is a contextually expected type, then the type that the inherent type requires for each table member provides the contextually expected type for the expressions in the row-list; otherwise there is no contextually expected type for these expressions.
There are two kinds of expression that can be used to construct an object: with an object constructor expression, the methods and fields of the constructed object are defined within the expression; with a new expression, the methods and fields come from a separate, named class definition.
object-constructor-expr := [annots] object-type-qualsobject
[type-reference] object-constructor-block object-constructor-block :={
object-member*}
object-member := object-field | method-defn | remote-method-defn | resource-method-defn
The resulting of evaluating an object-constructor-expr
is an
object. The object-constructor-expr
must specify
client
if any method-defn
includes a
remote-qual
.
The annotations applying to the object-constructor-expr
and its
members are evaluated when the object-constructor-expr
is
evaluated. This means that every object resulting from the evaluation of an
object-constructor-expr
has its own type descriptor.
If there is a type-reference
, then the referenced type is included
in the object's type in the same was as an object-type-inclusion
,
and the type-ids of the constructed object are type-ids of the referenced type.
If there is no type-reference
, then the applicable contextually
expected type T, if any, must be definite, and the type-ids of the constructed
object are the type-ids, if any, induced by T.
The object-constructor-expr
is implicitly read-only if the
applicable contextually expected type is a subtype of readonly or if the
type-reference
is present and references a type that is a subtype
of readonly. If the object-constructor-expr
is implicitly
read-only, then the object will be constructed with its read-only bit on;
furthermore, in this case an object-type-inclusion
is allowed to
directly or indirectly reference a readonly class.
The object-constructor-expr
is explicitly isolated if
object-type-quals
includes isolated
or if the
type-reference is present and references an isolated object type. An
object-constructor-expr
that is explicitly isolated will construct
an object with its isolated bit set.
object-visibility-qual :=public
|private
A visibility qualifier of private
can be used within an
object-constructor-expr
expression; this means that the visibility
region consists of every method-defn-body in the object-constructor-expr.
object-field := metadata [object-visibility-qual] [final
] type-descriptor field-name [=
field-initializer];
field-initializer := expression
An object-field
declares and initializes a field of the object.
If a field does not specify a field-initializer, then there must be an
init
method and the init
method must initialize the
field. The field-initializer must meet the requirements for an isolated function
unless the init
method is present and not declared as
isolated
. If the object-constructor-expr
is implicitly
read-only, then the contextually expected type for a field-initializer will be
the intersection of readonly and the type specified in the type-descriptor of
the object-field.
If final
is present, then the field must be assigned to exactly
once, either by its initializer or in the init
method.
An object-constructor-expr
will construct an object with its
read-only bit set if every object-field is declared as final
and
has a type-descriptor that is a subtype of readonly
. In this case,
the static type of the object-constructor-expr
will be intersected
with readonly
.
An object field is isolated if it is declared as final
and
has a type-descriptor that is a subtype of readonly
or
isolated object {}
, or if the object-constructor-expr
is implicitly read-only. An object-constructor-expr
is
implicitly isolated if it is implicitly readonly or if every object
field is isolated. An object-constructor-expr
that is implicitly
isolated will construct an object with its isolated bit set. In this case, the
static type of the object-constructor-expr
will be intersected with
isolated object {}
. If the object-constructor-expr
is
explicitly isolated, then every field-initializer
must be an
isolated expression and any field that is not isolated must be declared as
private.
method-defn :=
metadata object-visibility-qual method-quals
function
method-name function-signature method-defn-body
A method-defn
defines a method of an object.
method-defn-body := function-defn-body
Within a method-defn-body
, the fields and methods of the object are
not implicitly in-scope; instead the variable self
is bound to the
object and can be used to access fields and methods of the object. If the
object-constructor-expr
is explicitly isolated, then the
self
variable must be accessed only within the scope of a lock
statement, except when self
is part of a
field-access-expr
of the form self.f
, where
f
is the name of an isolated field.
If isolated-qual
is present, then the method has its isolated bit
set and its method-defn-body
must satisfy the requirements for an
isolated function.
remote-method-defn :=
metadata remote-method-quals
function
remote-method-name function-signature method-defn-body
A remote-method-defn
defines a remote method. This is allowed only
when an object-network-qual
is present in the
object-constructor-expr
.
resource-method-defn :=
metadata resource-method-quals
function
resource-method-name resource-path function-signature method-defn-body
A resource-method-defn
defines a resource method for one or more
resources. This is allowed only when an object-network-qual
is
present in the object-constructor-expr
. The semantics of
resource-method-defn
extends the semantics of a
resource-method-decl
by associating a function with each declared
resource method.
A network interaction object uses resource-method-defn
s to provide
a resource access operation. The inputs to the resource access operation are a
string giving the resource method name, a list of values for the resource path,
and a list of values for the argument list. The operation looks up the function
associated with the resource method name and resource path, and then invokes the
function with the supplied argument list.
The list of values supplied for the resource path are used to bind parameters
specified in the resource-path
before the function is invoked.
These parameters are in scope in the method-defn-body
. A parameter
in a resource-path-segment-param
will be bound to the value at the
corresponding position in the list; a parameter in a
resource-path-rest-param
will be bound to a list of all the values
from the corresponding position onwards.
The resource access operation of a service object is responsible for
implementing the delegation semantics of a resource method that returns a
service object. A resource-path
is delegated if it occurs
in a resource-method-defn
with a resource-method-name
of get
and a return value that includes a service object type that
has one or more resources. If a resource access operation has a resource path
r, which has a prefix p (which may be the entire path)
that is delegated, then the get
method that provides the delegation
is first called with no arguments but with resource parameters bound as usual
resulting in a value v; if v is an error, then the result
of the resource access operation is v; otherwise a resource access
operation is performed on the v with the resource path being
r with p removed.
A method-defn
with a method-name
of init
is used to initialize the object and is treated specially. The return type of
the init
method must be a subtype of the union of error and nil,
and must contain nil; if init
returns an error, it means that
initialization of the object failed.
The parameter list of an init
method within an
object-constructor-expr
must be empty.
At any point in the body of a init
method, the compiler
determines which fields are potentially uninitialized. A field is potentially
uninitialized at some point if that field does not have an initializer and it
is not definitely assigned at that point. It is a compile error if a
init
method:
self
variable other than to access or modify the value of a field.
If the object-constructor-expr
is explicitly isolated, then a field
can only be assigned to by an assignment-stmt
with a right hand
side that is an isolated expression. The requirement to access self
within a lock statement does not apply to the init
method.
The visibility of the init
method cannot be private
.
Any init
method is not part of the shape of an object, and so does
not affect when an object value belongs to a type. The init
method
can be called in a method-call-expr
only when the expression
preceding the .
is self
.
A missing init
method is equivalent to an isolated
init
method with no parameters and an empty body (which will always
return nil).
new-expr := explicit-new-expr | implicit-new-expr explicit-new-expr :=new
class-descriptor(
arg-list)
class-descriptor := identifier | qualified-identifier | stream-type-descriptor
A new-expr constructs a new object or stream. The class-descriptor in an explicit-new-expr must refer to a class or a stream type.
When the class-descriptor refers to a class, the explicit-new-expr allocates
storage for an object of the type defined by the class and initializes it by
passing the supplied arg-list to the init
method defined by the
class. It is a compile error if the type-descriptor if the arg-list does not
match the signature of the class's init
method. If the result of
calling the init
method is an error value e, then the result of
evaluating the explicit-new-expr is e; otherwise the result is the newly
initialized object. The explicit-new-expr is isolated if the type of the
init
is isolated and the expression for every argument in the
arg-list is isolated.
When the class-reference refers to a stream type stream<T,E>
,
the arg-list must either be empty or be a single argument belonging to object
type StreamImplementor<T,E?>. When the arg-list is empty, the result will
be an empty stream (i.e. a stream whose next method returns nil). When the
arg-list evaluates to a StreamImplementor object, the result will be a stream
that wraps that object. The explicit-new-expr is isolated if the the arg-list is
empty or if the expression for the single argument is isolated.
An explicit-type-expr specifying a class-descriptor T has static type T, except
that if T is an class type and the type of the init
method is
E?, where E is a subtype of error, then it has static type T|E.
implicit-new-expr :=new
[(
arg-list)
]
An implicit-new-expr is equivalent to an explicit-new-expr that specifies the
applicable contextually expected type as the class-descriptor. An
implicit-new-expr consisting of just new
is equivalent to
new()
. It is an error if the applicable contextually expected type
is not a class or stream type.
variable-reference-expr := variable-reference variable-reference := identifier | qualified-identifier | xml-qualified-name xml-qualified-name := xml-namespace-prefix NoSpaceColon identifier
A variable-reference can refer to a variable, a parameter, a constant (defined with a module constant declaration), a function, a type (defined with a module type definition) or a class (defined with a module class definition).
When the variable reference has a prefix and the prefix has been declared using an xmlns-decl rather than an import-decl, then the result of evaluating the variable-reference-expr is a string of the form:
{namespace-uri}local-name
where the namespace-uri comes from xml-namespace-uri specified in the xmlns-decl, and the local-name comes from the identifier following the colon.
If the variable-reference references a type defined with a module type definition or a class defined with a module class definition, then the result of evaluating the variable-reference-expr is a typedesc value for that type or class.
A variable-reference-expr is isolated if it refers to an identifier bound by a let-expr to an expression that is isolated.
A variable-reference-expr that refers to a variable declared by a
module-var-decl that includes isolated
is only allowed within a
lock-stmt.
field-access-expr := expression .
field-name
A field-access-expr is typically used to access a field of an object or a field of a record. More generally, it can be used to access a member of an object or a member of a mapping. The semantics depends on the static type T of expression. A field-access-expr where T is a subtype of xml is interpreted as an xml-required-attribute-access-expr.
If T is a subtype of the object basic type, then T must have a member with name
field-name. The field-access-expr is evaluated by first evaluating the
expression to get a value obj. If the member is a field, then the
result of the expression if the value of that field of obj and the
static type of the field-access-expr is the type of that field of T. If that
member is a method, then the result of the expression is a new function value
that when called will call the method with self
bound to
obj; the isolated bit of the new function value is set if and only if
the isolated bits of both obj and the method are set; the type
descriptor of the new function value will have the same annotations as the type
descriptor of the method.
Let T' be the intersection of T and basic type mapping, let K be the singleton type containing just the string field-name, and let M be the member type for K in T'.
If the following all apply:
then the field-access-expr is treated as an optional-field-access-expr. Otherwise, the rest of this section applies.
The compile-time requirements on the field-access-expr depend on whether the type descriptor describing T is lax:
The static type of field-access-expr is M|E, where E is empty if K is a required key type and T' is a subtype of T, and error otherwise (E can only be error in the lax case.) In the lax case, if M is lax, then the static type of the field-access-expr is lax even if E is an error.
A field-access-expr is evaluated as follows:
optional-field-access-expr := expression ?.
field-name
An optional-field-access-expr accesses a possibly undefined mapping member,
returning ()
if the member does not exist.
An optional-field-access-expr where the static type of expression
is
a subtype of xml is interpreted as an xml-optional-attribute-access-expr.
Let T be the static type of expression, let T' be the intersection of T and basic type mapping, let K be the singleton type containing just the string field-name and let M be the member type of K in T'. The compile-time requirements on the optional-field-access-expr depend on whether the type descriptor describing T is lax:
()
and the mapping basic type, and the type descriptor for T must
include field-name
as an individual-field-descriptor (if the type
descriptor is a union, then this requirement must be satisfied by at least one
member of the union).The static type of the optional-field-access-expr is M|N|E where
()
if ()
is a subtype of T or K is not a
required key type for T', and empty otherwise;error
if T' is not a subtype of T?, and empty otherwise (E
can only be error in the lax case).An optional-field-access-expr is evaluated as follows:
()
, the result is ()
()
xml-attribute-access-expr := xml-required-attribute-access-expr | xml-optional-attribute-access-expr xml-required-attribute-access-expr := expression.
xml-attribute-name xml-optional-attribute-access-expr := expression?.
xml-attribute-name xml-attribute-name := xml-qualified-name | qualified-identifier | identifier
An XML attribute access expression provides convenient access to an attribute of an XML element. It is a compile-time requirement that the static type of the expression is a subtype of xml.
A string representing the name of the attribute is computed at compile-time from the xml-attribute-name. When the xml-attribute-name is an identifier without a prefix, the attribute name string is the identifier. When the xml-attribute-name has a prefix, normally the xml-attribute-name is an xml-qualified-name, in which the prefix is an xml-namespace-prefix declared using an xmlns-decl. In this case, the xml-qualified-name is expanded at compile-time into an attribute name string of the form
{namespace-uri}local-name
where the namespace-uri comes from xml-namespace-uri specified in the xmlns-decl, and the local-name comes from the identifier following the colon.
It is also allowed for the xml-attribute-name to be specified as a
qualified-identifier, in which the prefix is a module-prefix declared using an
import-decl. In this case the qualified-identifier must refer to a
module-const-decl of type string, and the attribute name string is the value of
the referenced constant. This allows e.g. xml:lang
to work.
An xml-optional-attribute-access-expr is evaluated as follows. The expression is
evaluated resulting in an xml value v. If v is an empty
xml value, the result is ()
. Otherwise, if v is not a
singleton element, the result is an error. Otherwise, let m be that
element's attribute map and let k be the attribute name string
computed at compile-time from the xml-attribute-name. If m has a
member s with key k, the the result is s.
Otherwise, the result is ()
.
An xml-required-attribute-access-expr is evaluated the same as an
xml-optional-attribute-expr, except that for cases where the result of the
xml-optional-attribute-expr would be ()
, the result of the
xml-required-attribute-access-expr is an error.
The static type of an xml-required-attribute-access-expr is
string|error
The static type of an
xml-optional-attribute-access-expr is string|error|()
.
annot-access-expr := expression .@
annot-tag-reference
The annot-tag-reference must refer to an annotation tag declared with an
annotation declaration. The static type of expression must be a subtype of
typedesc
.
An annot-access-expr
is evaluated by first evaluating
expression
resulting in a typedesc value t. If t
has an annotation with the tag referenced by annot-tag-reference
,
then the result of the annot-access-expr
is the value of that
annotation; otherwise, the result is nil.
The static type of the annot-access-expr
is T? where T is the type
of the annotation tag.
member-access-expr := container-expression[
(key-expression | multi-key-expression)]
container-expression := expression key-expression := expression multi-key-expression := expression (,
expression)+
A member-access-expr accesses a member of a structured value using its key, or a constituent of a sequence value using its index.
The requirements on the static type of container-expression and key-expression are as follows:
K
and
the static type of key-expression must be a subtype of
K
, where the static type of container-expression is
table<R> key<K>
;
A multi-key-expression is allowed only when the static type of a
container-expression is a subtype of table. A multi-key-expression
E1, E2,..., En
is equivalent to a
list-constructor-expr [E1, E2,...,
En]
.
A member-access-expr is evaluated as follows:
()
; otherwise, the result is the member
of c with key k.()
or c
does not contain a member with key k, the result is ()
;
otherwise, the result is the member of c with key k.
Let T the static type of container-expression. If T is a subtype of string, then
the static type of the member-access-expr is string:Char
, that is the subtype of
strings containing strings of length 1. If T is a subtype of xml<M>, then
the static type of the member-access-expr is M|E, where E is the type of the
empty xml value. If T is a subtype of table<R>, then the static type of
member-access-expr is R?. Otherwise, let K be the static type of key-expression
and let M be the member type of K in T; if T contains nil, or T is a subtype of
mapping and K is an optional key type for T, then the static type of the
member-access-expr is M?, otherwise the static type is M.
function-call-expr := function-reference(
arg-list)
function-reference := variable-reference arg-list := positional-args [,
other-args] | [other-args] other-args := named-args | rest-arg
A function-call-expr is evaluated by constructing an argument list and passing
the argument list to the function referred to by the variable-name. The argument
list will be used to bind the parameters of the function before the body of the
function is executed. If the function terminates normally, then the result of
the function-call-expr is the return value of the function; otherwise the
function-call-expr completes abruptly with a panic. The static type of the
function-call-expr is the return type of the function type; this type is
permitted to be never
.
The function-reference must refer to a variable with function type. The type descriptor of that function type is used to construct an argument list from the specified arg-list. Note that it is the type descriptor of the declared type of the variable that is used for this purpose, rather than the runtime type descriptor of the referenced function value. The expressions occurring in the arg-list are evaluated in the order in which they occur in the arg-list; the contextually expected type for the expression comes from the static type required for the expression as specified below.
positional-args := positional-arg (,
positional-arg)*
positional-arg := expression
The result of evaluating the i-th positional-arg becomes the i-th member of the argument list. The static type of the expression for the i-th positional-arg must be a subtype of the type declared for the i-th parameter that is not a rest-param, if there is such a parameter; otherwise, there must be a rest-param and the static type must be a subtype of the type declared for the rest-param.
named-args := named-arg (,
named-arg)* named-arg := arg-name=
expression arg-name := identifier
The arg-name of every named-arg must be distinct. Each named-arg must correspond to either a parameter that is not a rest-param or to a field of an included-record-param. In the latter case, there are two possibilities.
never
.never
, the
record-type-descriptor must include an individual-field-descriptor that has type
never
and is optional, and so disallows a field with that
name;It is an error if there is a named-arg and a positional-arg that correspond to the same parameter. If there is a named-arg or positional-arg corresponding to an included-record-param, it is an error for a named-arg to specify a field of that included-record-param. The static type of the expression of the named-arg must be a subtype of the type declared for the corresponding parameter or field-descriptor.
rest-arg := ...
expression
The static type of the expression in a rest-arg must be either a list type or a mapping type. If it is a list type, then the rest-arg is equivalent to specifying each member of the list as a positional-arg. If it is a mapping type, then the rest-arg is equivalent to specifying each field of the mapping as a named-arg, with the name and value of the named-arg coming from the name and value of the field. In either case, the static type of the expression must be such as to ensure that the equivalent named-args or positional-args would be valid.
If there is neither a named-arg nor a positional-arg for a parameter that is not a rest-param, then a default value is computed for the parameter, if possible; otherwise, it is an error. Default values are computed in parameter declaration order.
<>
, then the value for the parameter will be a typedesc value
determined at compile-time from the contextually expected type of the
function-call-expr as follows. Let the name of the parameter for which a default
of <>
was specified be t. Let the type of
t be typedesc<T>. Let the return type descriptor for
the function be R, which will have references to t. Let
the contextually expected type for the function-call-expr be C. Then
the default value is found by unifying C and R: the
default value is a typedesc value that describes the maximal type S,
such that S is a subtype of T, and R with
S substituted for t is a subtype of C.
A function-call-expr is isolated if the type of the function being called is
isolated and the expression for every argument is isolated. In addition, a call
to the clone
or cloneReadOnly
functions defined by the
lang.value module of the lang library is always isolated.
When there is a need to call a function value resulting from the evaluation of an expression, the result of the expression evaluation can be assigned to a variable and then the function called by using a reference to the variable.
method-call-expr := expression.
method-name(
arg-list)
A method-call-expr
either calls an object's method, calls an
object's field where the type of the field is a function or calls a function in
the lang library. The static type of the method-call-expr is the return type of
the function type of the function that is called; this type is permitted to be
never
. The evaluation of the method-call-expr starts by evaluating
expression
resulting in some value v. There is no
contextually expected type for expression
.
If the static type of expression
is a subtype of object, and the
object type includes a method named method-name
, then the
method-call-expr
is executed by calling that method on
v. The arg-list
is used to construct an argument list
that is passed to the method in the same way as with a
function-call-expr
. A method-call-expr
cannot be used
to call a remote method. A remote method of a client object can be called by a
client-remote-method-call-action
. In this case, a method-call-expr
is isolated if the expression preceding .
is isolated, the
expression for each argument in the arg-list is isolated and the type of the
method being called is isolated.
Otherwise, if the static type of expression
is a subtype of object,
and the object type includes a field named method-name
with a
function type, then the method-call-expr
is executed by calling the
value of that field. The arg-list
is used to construct an argument
list that is passed to the method in the same way as with a
function-call-expr
. In this case, a method-call-expr is isolated if
the type of the function is isolated and the expression for each argument in the
arg-list is isolated.
Otherwise, the method-call-expr
will be turned into a call to a
function in the lang library m:method-name(expression, arg-list)
,
where m is an automatically created module prefix for a module lang.M of the lang
library, where M is selected as follows.
expression
is a subtype of some basic
type with identifier B, and the module lang.B contains a function
method-name
then M is B. The identifier for a basic type is the
reserved identifier used in type descriptors for subtypes of that basic type, as
listed in the Lang library section.value
.
It is a compile-time error if the resulting function call does not satisfy all
the constraints that would apply if it has been written explicitly as a
function-call-expr
. The method-call-expr
expression is
isolated under the same conditions that the corresponding
function-call-expr
would be isolated.
error-constructor-expr :=error
[error-type-reference](
error-arg-list)
error-arg-list := positional-arg [,
positional-arg] (,
named-arg)*
An error constructor constructs a new error value. If an error-type-reference is
specified, it must denote a type that is a subtype of error; the effect is the
same as making the contextually expected type be that specified by the
error-type-reference. If there is no applicable contextually expected type, then
it is the same as if there were a contextually expected type of
error
.
The applicable contextually expected type E must be definite. The type-ids of the constructed error value are those induced by E. Note that it is allowed for E to refer to an intersection-type-descriptor.
The first positional-arg is of type string and specifies the error message; the
second positional-arg, if present, is of type error?
, with a
default of nil, and specifies the cause.
Evaluating the error-constructor-expr constructs a new detail mapping. Each
named-arg specifies a field of the error detail mapping; the static type of each
named-arg must be a subtype of value:Cloneable
. The type descriptor
E implies a type descriptor D for the detail mapping. The arg-name of every
named-arg must be specified as the field-name of an individual-field-descriptor
occurring in D, unless there are no such field names. Fields with default
values will also be added to the detail record based on D in the same way as the
mapping-constructor-expr adds fields with default values based on the
contextually expected type. The contextually expected type for each named-arg is
determined from D in the same way as for a mapping-constructor-expr. The detail
mapping is constructed as immutable, with its members being the result of
applying the ImmutableClone abstract operation to the result of evaluating each
named-arg and every defaultable arg.
The stack trace in the constructed error value describes the execution stack at the point where the error constructor was evaluated.
anonymous-function-expr := explicit-anonymous-function-expr | infer-anonymous-function-expr
explicit-anonymous-function-expr := [annots] function-quals function
function-signature (block-function-body|expr-function-body)
Evaluating an anonymous-function-expr creates a closure, whose basic type is function. With an explicit-anonymous-function-expr, the type of the function is specified explicitly as usual with a function-signature. With an infer-anonymous-function-expr, the type of the function is inferred.
If a variable-reference-expr within an anonymous-function-expr refers to a local variable defined outside of the anonymous-function-expr, the closure will capture a reference to that variable; the captured reference will refer to the same storage as the original reference rather than to a copy.
infer-anonymous-function-expr := infer-param-list expr-function-body infer-param-list := identifier |(
[identifier (,
identifier)*])
An infer-anonymous-function-expr can only be used in a context where a function
type is expected. Both the types of the parameters and whether the function type
is isolated
are inferred from the expected function type. The
function type will be inferred to be transactional
if the
expr-function-body calls any functions with a transactional type.
The scope of the parameters is expr-function-body
.
The static type of the infer-anonymous-function-expr will be a function type
whose return type is the static type of the expression
in
expr-function-body
. If the contextually expected type for the
anonymous-function-expr
is a function type with return type T, then
the contextually expected type for expression
in
expr-function-body
is T.
let-expr :=let
let-var-decl [,
let-var-decl]*in
expression let-var-decl := [annots] typed-binding-pattern=
expression
A let-expr binds variables and then evaluates an expression with those variables in scope.
A let-expr let T B = E1 in E2
is evaluated as
follows. E1 is evaluated resulting in a value v. The typed binding
pattern T B is matched to v, causing assignments to the variables occuring in B.
Then E2 is evaluated with those variables in scope; the resulting
value is the result of the let-expr.
A let-expr let D1, D2,...,Dn in E
is transformed into let D1 in let D2 in ... let
Dn in E
.
The typed-binding-pattern is used unconditionally, meaning that it is a compile
error if the static types do not guarantee the success of the match. If the
typed-binding-pattern uses var
, then the type of the variable is
inferred from the precise static type of the expression following
=
.
Since expressions cannot modify variables, the variables bound in a let-var-decl
are implicitly final. A let-expr is isolated if the expression following
in
is isolated; a reference in that expression to a variable bound
by a let-var-decl will be treated as isolated if the expression initializing the
variable in the let-var-decl is isolated.
type-cast-expr :=<
type-cast-param>
expression type-cast-param := [annots] type-descriptor | annots
A type-cast-expr
casts the value resulting from evaluating
expression
to the type described by the type-descriptor, performing
a numeric conversion if required.
Normally, the parameter for a type-cast-expr includes a type-descriptor. However, it is also allowed for the parameter to consist only of annotations; in this case, the only effect of the type cast is for the contextually expected type for expression to be augmented with the specified annotations. The rest of this subsection describes the normal case, where the type-cast-expr includes a type-descriptor.
A type-cast-expr is evaluated by first evaluating expression
resulting in a value v. Let T be the type described by
type-descriptor
. If v belongs T, then the result of the
type-cast-expr is v. Otherwise, if T includes shapes from exactly one basic
numeric type N and v belongs to another basic numeric type, then let n be
NumericConvert(N, v); if n is not an error and n belongs to T, then the result
of the type-cast-expr is n. Otherwise, the evaluation of the type-cast-expr
completes abruptly with a panic.
Let T be the static type described by type-descriptor
, and let TE
be the static type of expression
. Then the static type of the
type-cast-expr
is the intersection of T and TE', where TE' is TE
with its numeric shapes transformed to take account of the possibility of the
numeric conversion specified in the previous paragraph.
It is a compile-time error to attempt to use a type cast to
cast away an error: if the intersection of TE and error
is
non-empty, then the intersection of T and error
must also be
non-empty. A checkpanic
expression can be used instead to assert
that the result of an expression will never be an error.
The contextually expected type for expression
is the intersection
of the contextually expected type of the type-cast-expr
and the
type described by the type-descriptor
.
Note that a type-cast-expr
of <readonly>
can be
used both to cause constructors withing the expression
to construct
values with the read-only bit on and to verify that the value resulting from
the evaluation of expression
has its read-only bit on.
typeof-expr := typeof
expression
The result of a typeof-expr
is a typedesc value for the runtime
type of v, where v is the result of evaluating expression
.
The runtime type of v is the narrowest type to which v belongs.
typeof
with a simple
value produces a new typedesc value.typeof
with an
identical reference value produces an identical typedesc value. The type
descriptor resulting from typeof
will be the same as the type
descriptor used to construct the value. For containers, this is the same as the
inherent type; when the container is immutable, it will be a singleton type. For
an object, this is the same as the type descriptor used with new. For an error,
this is the same as the type descriptor used in the functional-constructor-expr.
Any annotations that were attached to the type descriptor used to construct the
value will this be available on the constructed value.
The static type of typeof-expr
is typedesc<T>, where T is the
static type of expression
.
unary-numeric-expr :=+
expression |-
expression |~
expression
The operators occurring in a unary-numeric-expr
have both lifted
form, which is specified in the Nil lifting section,
and an underlying form, which is specified in this section.
The unary -
operator performs negation. The static type of the
operand must be a number; the static type of the result is the basic type of the
static type of the operand, modified by the usual singleton typing rules. The semantics for each
basic type are as follows:
If the contextually expected type for a -
expression is T, then the
contextually expected type for the operand expressions is T', where a value v is
in T' if it belongs to int, decimal or float, and T contains a value with the
same basic type as v.
The unary +
operator returns the value of its operand expression. The static
type of the operand must be a number, and the static type of the result is the
same as the static type of the operand expression, modified by the usual singleton typing rules.
The ~
operator inverts the bits of its operand expression. The
static type of the operand must be int, and the static type of the result is
int, modified by the usual singleton typing
rules.
unary-logical-expr := !
expression
The !
operator performs logical negation. The static type of the
operand expression must be boolean. The !
operator returns true if
its operand is false and false if its operand is true. The static type of the
result is boolean, modified by the usual singleton
typing rules.
multiplicative-expr := expression*
expression | expression/
expression | expression%
expression
The *
operator performs multiplication; the /
operator
performs division; the %
operator performs remainder. These
operators have both lifted form, which is specified in the Nil lifting section, and an underlying form, which is
specified in this section.
The static type of each operand must be a subtype of int
,
float
or decimal
. If the static type of both operands
is not a subtype of the same basic type, then either the second operand must be
a subtype of int
, or the operator must be *
and the
first operand must be a subtype of int
; in either case, the
int
operand is first converted to the type of the other operand.
The operation performed depends on the basic type of the operands as follows:
*
performs integer multiplication; a panic will occur on overflow/
performs integer division, with any fractional part discarded
ie with truncation towards zero; a panic will occur on division by zero or
overflow, which happens if the first operand is -263 and the second
operand is -1%
performs integer remainder consistent with integer division,
so that if x/y
does not result in a panic,
then (x/y)*y +
(x%y)
is equal to x
; a
panic will occur if the second operand is zero; if the first operand is
-263 and the second operand is -1, then the result is 0*
performs the multiplication operation with the destination
format being the same as the source format, as defined by IEEE 754-2008; this
never causes a panic to occur/
performs the division operation with the destination format
being the same as the source format, as defined by IEEE 754-2008; this never
causes a panic to occur%
performs a remainder operation; the remainder is not the
IEEE-defined remainder operation but is instead a remainder operation analogous
to integer remainder; more precisely,
x
is NaN or y
is NaN or
x
is an infinity or y
is a zero,
then x % y
is NaNx
, and infinite y
, x %
y
is x
x
and finite non-zero
y
, x % y
is equal to
the result of rounding x - (y × n)
to the nearest representable value using the roundTiesToEven rounding mode,
where n
is the integer that is nearest to the
mathematical quotient of x
and y
without exceeding it in magnitude; if the result is zero, then its sign is that
of x
The static type of the result of a multiplicative-expr is the basic type of its
subexpressions, after conversion, modified by the usual singleton
typing rules, except that the result type is always float
in
the case where the operator is /
and the right-hand operand is
singleton float 0.0
. (This is a consequence of the fact that
0.0 == -0.0
, but 1.0/0.0 != 1.0/-0.0
.)
If the contextually expected type for a multiplicative-expr is T, then the contextually expected type for both operand expressions is T', where a value v is in T' if it belongs to int, decimal or float, and T contains a value with the same basic type as v.
additive-expr := expression+
expression | expression-
expression
The +
operator is used for both addition and concatenation; the
-
operator is used for subtraction. These operators have both
lifted form, which is specified in the Nil lifting
section, and an underlying form, which is specified in this section.
It is required that either:
The semantics for each basic type is as follows:
+
performs integer addition; a panic will occur on
overflow-
performs integer subtraction; a panic will occur on
overflow+
performs the addition operation with the destination format
being the same as the source format, as defined by IEEE 754-2008; this never
causes a panic to occur-
performs the subtraction operation with the destination
format being the same as the source format, as defined by IEEE 754-2008; this
never causes a panic to occurIf the contextually expected type for an additive-expr is T, then the contextually expected type for both operand expressions is T', where a value v is in T' if it belongs to int, decimal, float, string or xml and T contains a value with the same basic type as v.
shift-expr := expression<<
expression expression>>
expression expression>>>
expression
A shift-expr performs a bitwise shift. The shift operators have both lifted form, which is specified in the Nil lifting section, and an underlying form, which is specified in this section.
Both operand expressions must have static type that is a subtype of int. The left hand operand is the value to be shifted; the right hand value is the shift amount; all except the bottom 6 bits of the shift amount are masked out (as if by x & 0x3F). Then a bitwise shift is performed depending on the operator:
<<
performs a left shift, the bits shifted in on the
right are zero>>
performs a signed right shift; the bits shifted in on the
left are the same as the most significant bit>>>
performs a unsigned right shift, the bits shifted in on the
left are zero
If the operator is >> or >>> and the left hand operand is a
subtype of int:UnsignedK
when K
is 8, 16 or 32, then the static type of the result is
int:UnsignedN
where N
is the
smallest such K
; otherwise, the static type of the
result is int
. In all cases the static type of the result is
modified by the usual singleton typing rules.
range-expr := expression...
expression | expression..<
expression
The result of a range-expr is a new object belonging to the object type Iterable<int,()> that will iterate over a sequence of integers in increasing order, where the sequence includes all integers n such that
...
, less than or equal to the value of the
second expression..<
, less than the value of the second
expressionIt is a compile error if the static type of either expression is not a subtype of int.
A range-expr is designed to be used in a foreach statement, but it can be used elsewhere.
A range-expr is always isolated.
relational-expr := expression<
expression | expression>
expression | expression<=
expression | expression>=
expression
A relational-expr tests the relative order of two values. There must be an ordered type to which the static type of both operands belong. The static type of the result is boolean, modified by the usual singleton typing rules.
A relational-expr is evaluated by first evaluating the two expressions, resulting in values x and y. The result of relational-expr is true if and only if the result of the Compare(x,y) operation is
<
>
<=
>=
and otherwise false.
is-expr := expressionis
type-descriptor | expression!
is
type-descriptor
An is-expr tests whether a value belongs to a type.
An is-expr is evaluated by evaluating the expression yielding a result v.
If the operator is is
, then the result of the expression is true if
v belongs to the type denoted by the type-descriptor, and otherwise false. If
the operator is !is
, then the result of the expression is false if
v belongs to the type denoted by the type-descriptor, and otherwise true.
equality-expr := expression==
expression | expression!=
expression | expression===
expression | expression!==
expression
An equality-expr tests whether two values are equal. For all four operators, it is a compile time error if the intersection of the static types of the operands is empty.
The === operator tests for exact equality. The !== operator results in the negation of the result of the === operator. Two values v1 and v2 are exactly equal if they have the same basic type T and
The == operator tests for deep equality. The != operator results in the negation of the result of the == operator. For both == and !=, at least one of the operands must have a static type that is a subtype of anydata. Two values v1, v2 are deeply equal if DeepEquals(v1, v2) is true.
The static type of the result of expression is boolean; in the case of the == and != operators, the static type is modified by the usual singleton typing rules; in the case of the === and !== operators, the static type is not so modified.
Note that === and == are the same for simple values except for floating point types.
For the float type, the operators differ as regards floating point zero: == treats positive and negative zero from the same basic type as equal whereas === treats them as unequal. Both == and === treat two NaN values from the same basic type as equal. This means that neither of these operators correspond to operations defined by IEEE 754-2008, because they do not treat NaN in the special way defined for those operations.
For the decimal type, the operators differ in whether they consider the
precision of the value. For example, 1.0 == 1.00
is true but
1.0 === 1.00
is false.
binary-bitwise-expr := bitwise-and-expr | bitwise-xor-expr | bitwise-or-expr bitwise-and-expr := expression&
expression bitwise-xor-expr := expression^
expression bitwise-or-expr := expression|
expression
A binary-bitwise-expr does a bitwise AND, XOR, or OR operation on its operands. These operators have both lifted form, which is specified in the Nil lifting section, and an underlying form, which is specified in this section.
The static type of both operands must be a subtype of int. The static type of the result is as follows:
int:UnsignedK
when K
is 8, 16 or
32, then the static type of the result is int:UnsignedN
where N
is the smallest such K
;
otherwise, the static type of the result is int
;int:UnsignedK
when K
is 8, 16 or 32, then the static type of the result is
int:UnsignedN
where N
is the
smallest such K
; otherwise, the static type of the result
is int
.In all cases, the static type of the result is modified by the usual singleton typing rules.
logical-expr := logical-and-expr | logical-or-expr logical-and-expr := expression&&
expression logical-or-expr := expression||
expression
The static type of each expression in a logical-expr must be a subtype of boolean.
A logical-and-expr is evaluated as follows:
false
, then the result of the
logical-and-expr is x, and the right-hand expression is not
evaluated;A logical-or-expr is evaluated as follows:
true
, then the result of the
logical-or-expr is x, and the right-hand expression is not
evaluated;The static type of the result of a logical-expr is boolean, modified by the usual singleton typing rules. Furthermore:
conditional-expr := ternary-conditional-expr | nil-conditional-expr ternary-conditional-expr := expression?
expression:
expression nil-conditional-expr := expression?:
expression
A ternary-conditional-expr C ?
T :
F is evaluated as
follows:
In a ternary-conditional-expr C ?
T :
F, the static
type of C must be a subtype of boolean, and the static type of the result is
determined from ther static types of C, T and F as follows:
A nil-conditional-expr L ?:
R is evaluated as follows:
The static type of the result of L ?:
R is determined from the
static types of L and R as follows:
checking-expr := checking-keyword expression checking-keyword :=check
|checkpanic
Evaluates expression resulting in value v. If v has basic type error, then
check
, then the check-expression
completes abruptly with a check-fail with associated value v;checkpanic
, then the check-expression
completes abruptly with a panic with associated value v.
If the static type of expression Expr
is T|E, where E is
a subtype of error, then the static type of check Expr
or checkpanic Expr
is T. However, if:
check
,Expr
is lax,json
, and()|boolean|int|float|decimal|string
,
then instead check Expr
is treated as check
val:ensureType(Expr, s)
, where
s
is a typedesc value representing S and
val
refers to the value
module in langlib.
The trap expression stops a panic and gives access to the error value associated with the panic.
trap-expr := trap
expression
Semantics are:
expression
resulting in value v
expr
is T, then type of trap expr
is
T|error. It is permitted for T to be never
.A query expression provides a language-integrated query feature using SQL-like syntax. In this version of Ballerina, the functionality is similar to a list comprehensions; future versions will provided richer functionality.
query-expr := [query-construct-type] query-pipeline select-clause [on-conflict-clause] query-construct-type :=map
|table
key-specifier |stream
query-pipeline := from-clause intermediate-clause* intermediate-clause := from-clause | where-clause | let-clause | join-clause | order-by-clause | limit-clause
A query expression consists of a sequence of clauses. The semantics of clauses
is specified in terms of transforming a sequence of frames, where a frame is a
binding of variables to values. The input to each clause is a sequence of
frames. As each clause is executed, it iterates over its input frames and emits
output: the final clause, which is a select
clause, emits output
values; the other clauses emit output frames. When a
query-expr is evaluated, its clauses are executed in a pipeline by making the
sequence of frames emitted by one clause be the input to the next clause. Each
clause in the pipeline is executed lazily, pulling input from its preceding
clause. The input to the first clause is a single empty frame.
When a from
or join
clause is executed, it makes calls
to the next
method of an Iterator object. If any of these calls
return an error value e, then we say that the clause completes
early with error value e.
A query-expr that does not construct a stream is evaluated as described in this paragraph. If a clause completes early with error value e, then the result of evaluating the query-expr is e. The normal rules apply if the execution of any clause results in the evaluation of an expression completing abruptly. Otherwise, the result of evaluating a query expression is a single value, which is constructed from the sequence of values emitted by the last clause. The result must be one of the following basic types:
A query-expr that constructs a stream must start with the stream
keyword and is evaluated differently from a query-expr that constructs a value
of other basic types. The clauses in the query-expr are executed lazily: clauses
in the query-expr becomes closures, which are called as a result of next
operations being performed on the stream. If a next operation causes the
execution of a clause that completes early with an error value, then the error
value is returned by the next operation; the evaluation of the query-expr will
already have resulted in a stream value. If the next operation results in the
evaluation of an expression within the query-expr completing abruptly with a
check-fail, the associated error value will be returned as the result of the
next operation. If the next operation results in the evaluation of an expression
within the query-expr completely abruptly with a panic, then the next operation
will complete abruptly with a panic.
A query-expr that constructs a mapping must start with the map
keyword.
An emitted value [k, v]
results in adding a
field with key k
and value v
.
If the query-expr starts with table
, then the query-expr will
construct a table; the key-specifier specifies the key sequence of the
constructed table in the same way as with a table-constructor-expr. Otherwise,
the applicable contextually expected type determines the basic type of the value
constructed. If there is no contextually expected type, then the basic type of
the value constructed is the basic type of expression following in
;
it is a compile-time error if the static type of this expression is not a
subtype of one of the basic types that a query expression can construct.
When the query-expr is constructing a value of basic type list, mapping, table or xml, and the applicable contextually expected type is a subtype of readonly, then the query-expr will construct a value with its read-only bit set and the static type of the query-expr will be a subtype of readonly. Furthermore, the contextually expected type for the expression in the select-clause will also be a subtype of readonly.
When during the construction of a map or table, an emitted value is added as a new
member, it replaces any existing member with the same key value; when a new
member replaces an existing member, it will have the same position in the order
of members as the existing member. This behavior may be controlled by an
on conflict
clause.
Variables bound by the clauses of a query-pipeline are implicitly final, and cannot be modified.
from-clause :=from
typed-binding-patternin
expression
A from
clause is executed as follows:
in
expression with f in scope resulting
in an iterable value c;i.next()
resulting in a value r;()
, stop loop L;r.value
;where-clause := where
expression
A where
clause is executed as follows:
expression
with f in scope resulting in
value b;let-clause :=let
let-var-decl [,
let-var-decl]*
A let
clause consisting of a single let-var-decl is executed as follows:
A let
clause with more than one let-var-decl
is
transformed into multiple let
clauses: let x1 =
E1, x2 = E2
is transformed into
let x1 = E1 let x2 =
E2
.
join-clause := [outer
]join
typed-binding-patternin
expression join-on-condition join-on-condition :=on
expressionequals
expression
A join clause performs an inner or left outer equijoin.
A join
clause is executed as follows:
in
resulting in an iterable
value c;i.next()
resulting in a value r;()
, stop loop L;r.value
;equals
with f
in scope resulting in a value k;equals
with frame f
in scope resulting in a value k;outer
was specified and fs is empty, then emit a
single frame consisting of f augmented with a frame that binds every
variable occurring in typed-binding-pattern to ()
.
In the above, if c evaluates to a table, and the result of evaluating
the expression to the right of equals
for a member of the table
will always be the key value for that member of the table, then the above can be
optimized by using the table instead of creating a new mapping.
Variables bound by previous clauses are not in scope for the expression
following in
, nor for the expression on the right of
equals
. Variables bound by the typed-binding-pattern are not in
scope for the expression following in
, nor for the expression on
the left of equals
.
When outer
is specified, the typed-binding-pattern is treated
specially: the inferable-type-descriptor in the typed-binding-pattern must be
var
, and for each variable occurring in the typed-binding-pattern,
if the type that would be inferred usually for the variable would be
T, then the type inferred in this case will be
T?
.
order-by-clause :=order
by
order-key [,
order-key]* order-key := expression [order-direction] order-direction :=ascending
|descending
The static type of the expression in an order-key must be an ordered type. The order-direction is ascending
if not explicitly specified.
An order-by
clause is executed by constructing a list
of entries, where each entry consists of:
In order to define the correct ordering of the list, we first define a CompareK operation that compares two keys with a specified direction: CompareK(x,y,d) is CompareA(x,y) if d is ascending and otherwise (d is descending) is Reverse(CompareD(x,y)), where Reverse applied to LT, EQ, GT is GT, EQ, LT respectively. Now define an operation CompareKL(x,y,d) applying to two lists of keys and a list of directions as follows. Let x be [x1,x2,...,xn], y be [y1,y2,...,yn], and d be [d1, d2,...,dn]. Then let r be CompareK(x1,y1,d1). Then the result of CompareKL(x,y,d) is:
Then in a correct ordering of the list of entries when the list of directions is d, an entry with keys with keys x and index i is before an entry with keys y and index j if CompareKL(x,y,d) is LT or if CompareKL(x,y,d) is EQ and i is less than j.
An order-by
clause is executed as follows:
limit-clause := limit
expression
A limit
clause limits the number of frames emitted by a query pipeline.
A limit
clause is executed as follows:
The static type of expression must be a subtype of int
.
select-clause := select
expression
A select
clause is executed as follows:
on-conflict-clause :=on
conflict
expression
An on conflict
clause is allowed only for a query expression that
constructs a map or table with a key sequence. The expression is evaluated when the
select
clause emits a value that conflicts with a previous value,
in the sense that both values have the same key value in the map or table. The
expression is evaluated with the same frame in scope as the select clause that
emitted the value that conflicts with the previous value. The static type of the
expression must be a subtype of error?
. If the result of evaluating
the expression is an error e, then the result of evaluating the
query-expr is e. Otherwise, the result must be nil, and the earlier
new value replaces the earlier conflicting value, in the same way as if there
not been an on conflict
clause.
Note that the expression may have side-effects; for example, it may call a function that logs an error.
XML navigation expressions allow for convenient navigation of XML element structure, in a similar way to XPath.
xml-filter-expr | xml-step-expr:=
xml-name-pattern := xml-atomic-name-pattern [|
xml-atomic-name-pattern]* xml-atomic-name-pattern :=*
| identifier | xml-namespace-prefix NoSpaceColon identifier | xml-namespace-prefix NoSpaceColon*
An XML name pattern matches a string specifying the name of an XML element.
An xml-atomic-name-pattern that is *
matches any name.
An xml-namespace-prefix in an xml-atomic-name-pattern must be declared by an xmlns-decl. If there is an in-scope default namespace (declared by an xmlns-decl), an xml-atomic-pattern that is just an identifier specifies a name in that default namespace.
xml-filter-expr := expression.<
xml-name-pattern>
An xml-filter-expr selects constituents of a sequence that are elements with a
name matching a specified name pattern. The static type of the expression must
be a subtype of xml. The static type of the xml-filter-expr is
xml<xml:Element>
.
An xml-step-expr provides access to the children or descendants of an element, similar to a location path in XPath.
xml-step-expr := expression xml-step-start xml-step-extend* xml-step-start := xml-all-children-step | xml-element-children-step | xml-element-descendants-step xml-all-children-step :=/*
xml-element-children-step :=/<
xml-name-pattern>
xml-element-descendants-step :=/**/<
xml-name-pattern>
xml-step-extend :=.<
xml-name-pattern>
|[
expression]
|.
method-name(
arg-list)
The production for xml-step-expr is ambiguous with the productions for member-access-expr and method-call-expr: this must be resolved by preferring the parse that includes as much as possible in the xml-step-expr.
The static type of the expression must be a subtype of xml.
An xml-step-expr that starts with an xml-all-children-step
E /* X
is equivalent to
xml:map(xml:elements(E), v => xml:getChildren(v) X)
where v
is a variable name not used in X
.
An xml-step-expr that starts with an xml-element-children-step
E /< NP > X
is equivalent to
xml:map(xml:elements(E), v => xml:getChildren(v) .<NP> X)
where v
is a variable name not used in X
.
An xml-step-expr that starts with an xml-element-descendants-step
E /**/< NP > X
is equivalent to
xml:map(xml:elements(E), v => xml:getDescendants(v) .<NP> X)
where v
is a variable name not used in X
.
transactional-expr := transactional
A transactional-expr
evaluates to true if the expression is being
evaluated in transaction mode and false otherwise.
action := start-action | wait-action | send-action | receive-action | flush-action | client-remote-method-call-action | client-resource-access-action | query-action | commit-action action-or-expr := expr-only | action | type-cast-action-or-expr | checking-action-or-expr | trap-action-or-expr | query-action-or-expr |(
action-or-expr)
type-cast-action-or-expr :=<
type-cast-param>
action-or-expr checking-action-or-expr := checking-keyword action-or-expr trap-action-or-expr :=trap
action-or-expr
Actions are an intermediate syntactic category between expressions and statements. Actions are similar to expressions, in that they yield a value. However, an action cannot be nested inside an expression; it can only occur as part of a statement or nested inside other actions. This is because actions are shown in the sequence diagram in the graphical syntax.
As with the grammar for expression, the grammar for action-or-expr is ambiguous.
The precedence for action-or-expr is the same as for expression, with the
addition that the ->
operator used in a
client-remote-method-call-action has the same precedence as the .
operator used in a method-call-expr.
An action is evaluated in the same way as an expression. Static typing for actions is the same as for expressions.
A type-cast-action-or-expr
, checking-action-or-expr
or
trap-action-or-expr
is evaluated in the same way as a
type-cast-expr
, checking-expr
or
trap-expr
respectively.
query-action-or-expr := [query-construct-type] query-action-pipeline select-action-clause [on-conflict-clause] query-action-pipeline := from-action-clause intermediate-action-clause* intermediate-action-clause := from-action-clause | let-action-clause | where-clause | join-clause | order-by-clause | limit-clause from-action-clause :=from
typed-binding-patternin
action-or-expr let-action-clause :=let
let-action-var-decl [,
let-action-var-decl]* let-action-var-decl := [annots] typed-binding-pattern=
action-or-expr select-action-clause :=select
action-or-expr
A query-action-or-expr
is a query-expr
in which
certain subexpressions are allowed to be an action-or-expr
. It is
evaluated in the same way as a query-expr
.
Ballerina's concurrency model supports both threads and coroutines. A Ballerina program is executed on one or more threads. A thread may run on a separate core simultaneously with other threads, or may be pre-emptively multitasked with other threads onto a single core.
Each thread is divided into one or more strands. No two strands belonging to the same thread can run simultaneously. Instead, all the strands belonging to a particular thread are cooperatively multitasked. Strands within the same thread thus behave as coroutines relative to each other. A strand enables cooperative multitasking by yielding. When a strand yields, the runtime scheduler may suspend execution of the strand, and switch its thread to executing another strand. The following actions cause a strand to yield:
In addition, any function with an external-function-body can potentially yield;
it should only do so if it performs a system call that would block or calls a
Ballerina function that itself yields. The sleep
function in
lang.runtime
will make the strand yield. Other functions in the
lang library do not themselves yield, although if they call a function passed as
an argument, that function may result in yielding.
There are two language constructs whose execution causes the creation of new strands: named-worker-decl and start-action. Except in the cases explicitly allowed by this specification, the new strand must be part of the same thread as the current strand. An implementation may also allow annotations to be used to indicate that the newly created strand should be in a separate thread from the current strand.
Before the block-function-body of a function-defn is executed, the parameters declared in the function-signature of the function-defn are initialized from the argument list that was constructed by the function-call-expr. The non-rest parameters are initialized from the arguments in the argument list in order. The conformance of the argument list to the param-list declared for the function ensures that each parameter will be initialized to a value that belongs to the declared type of the parameter. If there is a rest-param, then that is a initialized to a newly created list containing the remaining arguments in the argument-list; the inherent type of this list will be T[] where T is the type of the rest-param. The conformance of the argument list ensures that the members of this list will belong to type T.
The parameters are in scope for the block-function-body. They are implicitly final: they can be read but not modified.
block-function-body :={
[default-worker-init named-worker-decl+] default-worker}
default-worker-init := statement* default-worker := statement* named-worker-decl := [annots] [transactional-qual]worker
worker-name return-type-descriptor statement-block worker-name := identifier
A worker represents a single strand of a function invocation. A statement is always executed in the context of a current worker. A worker is in one of three states: active, inactive or terminated. When a worker is in the terminated state, it has a termination value. A worker terminates either normally or abnormally. An abnormal termination results from a panic, and in this case the termination value is always an error. If the termination value of a normal termination is an error, then the worker is said to have terminated with failure; otherwise the worker is said to have terminated with success. Note that an error termination value resulting from a normal termination is distinguished from an error termination value resulting from an abnormal termination.
A function always has a single default worker, which is unnamed. The strand for a function's default worker is the same as the strand of the worker on which function was called. When a function is called, the current worker becomes inactive, and a default worker for the called function is started. When the default worker terminates, the function returns to its caller, and the caller's worker is reactivated. Thus only one worker in each strand is active at any given time. If a function's default worker terminates normally, then its termination value provides the return value of the function. If a function's default worker terminates abnormally, then the evaluation of the function call expression completes abruptly with a panic and the worker's termination value provides the associated value for the abrupt completion of the function call. The function's return type is the same as the return type of the function's default worker.
A function also has zero or more named workers. Each named worker runs on its own new strand. The strand can run on a separate thread from the default worker if the function is isolated. The termination of a function is independent of the termination of its named workers. The termination of a named worker does not automatically result in the termination of its function. When a function's default worker terminates, causing the function to terminate, the function does not automatically wait for the termination of its named workers.
A named worker has a return type. If the worker terminates normally, the termination value will belong to the return type. If the return type of a worker is not specified, it defaults to nil as for functions.
A named-worker is a transactional scope only if it is declared as
transactional
. This is allowed only if the block-function-body is a
transactional scope, i.e. if it is the body of a function declared as
transactional
.
When a function has named workers, the function's default worker executes in three stages, as follows:
Parameters and variables declared in default-worker-init are in scope within named workers, whereas variables declared in default-worker are not.
The execution of a worker's statements may result in the execution of a
statement that causes the worker to terminate. For example, a return statement
causes the worker to terminate. If this does not happen, then the worker
terminates as soon as it has finished executing its statements. In this case,
the worker terminates normally, and the termination value is nil. In other
words, falling off the end of a worker is equivalent to return;
,
which is in turn equivalent to return ();
.
The scope of a worker-name in a named-worker-decl that is part of a block-function-body is the entire block-function-body with the exception of the default-worker-init. When a worker-name is in scope, it can be used in a variable-reference-expr. The result of evaluating such a variable reference is a future value that refers to the value to be returned by that named worker. The static type of the result is future<T>, where T is the return type of the worker.
A strand can initiate a wait on another strand by using a wait-action with a value of type future. Only one wait on a strand can succeed; this wait receives the value returned by the strand. Any other waits on that strand fail. It is a compile-time error if for any named worker it is possible for the name of that worker to be evaluated as a variable-reference more than once for any execution of that worker. This ensures that wait operations that use just a worker-name to identify the strand to wait on cannot fail at runtime.
In the above, function includes method, and function call includes method call.
expr-function-body := =>
expression
An expr-function-body is used for an expression-bodied function, that is a
function whose body is specified by an expression. An expr-function-body of the
form => E
is short for a block-function-body {
return E}
.
external-function-body :==
[annots]external
An external-function-body
means that the implementation of the
function is not provided in the Ballerina source module. A function-defn-body
with a function-signature that is dependently-typed must be an
external-function-body.
The body of a function or method declared as isolated
must meet the
following requirements.
final
or configurable
but does not include
isolated
, or is declared by a listener-decl or a service-decl,
andisolated object
{}
or of readonly
(which will always be the case if the
identifier is declared as configurable
).isolated
.readonly|isolated object {}
, where a variable reference
is captured if it occurs within an anonymous-function-expr or named-worker-decl
and refers to a variable declared within the function but outside of that
anonymous-function-expr or named-worker-decl.
If an isolated function has an external-function-body
, then the
above requirements also apply to the external implementation as regards its
access to the mutable state of the Ballerina program, in the sense that it must
only access the mutable state that is reachable from the parameters passed to
the function. In addition, the function should have well-defined behaviour if it
is called concurrently on multiple strands.
statement := action-stmt | local-var-decl-stmt | xmlns-decl-stmt | assignment-stmt | compound-assignment-stmt | destructuring-assignment-stmt | call-stmt | checking-stmt | if-else-stmt | regular-compound-stmt | continue-stmt | break-stmt | stmt-with-on-fail | fail-stmt | retry-stmt | transaction-stmt | retry-transaction-stmt | rollback-stmt | panic-stmt | return-stmt | fork-stmt
regular-compound-stmt := do-stmt | match-stmt | foreach-stmt | while-stmt | lock-stmt
A regular-compound-stmt
can be combined with an
on-fail-clause
to make a stmt-with-on-fail
.
The execution of a statement completes in one of three ways:
When the execution of a statement involves the evaluation of an action or an expression, and that evaluation completes abruptly, then, except where explicitly stated to the contrary, the execution of the statement completes as follows.
fail-stmt
with an expression
that evaluates to e.panic-stmt
with an
expression that evaluates to e.The following sections describe for each kind of statement any other cases in which the execution of that kind of statement does not completely normally.
statement-block :={
statement*}
A statement-block
is executed by executing its
statement
s sequentially. This means that when the execution of one
statement
completes normally, the immediately following
statement
is executed. However, when the executon
statement
does not complete normally, then none of the following
statement
s in the statement-block
are executed. The
execution of the statement-block
completes normally if and only if
the execution of all of its statement
s completes normally.
A statement is reachable if the analysis described in this section
determines that there is a possibility that the statement will be executed. It
is a compile error if a statement
other than a
panic-stmt
is not reachable.
The analysis proceeds by determining recursively for each statement whether it
is possible for the statement to complete normally, assuming that the statement
is reachable. Each statement
in a statement-block
after the first statement
is reachable only if it is possible for
all of the preceding statement
s to complete normally.
When the execution of a statement or a transfer of control is conditional on the result of evaluating an expression, then the analysis assumes that it is possible for the statement to be executed or control to be transferred, unless the static type of the expression makes it impossible. Similarly, when the evaluation of a subexpression is conditional on the result of previously evaluating another subexpression, then the analysis assumes that the second evaluation is possible unles the static type of the first subexpression makes it impossible.
fork-stmt :=fork
{
named-worker-decl+}
The fork statement starts one or more named workers, which run in parallel with each other, each in its own new strand. The strand can run on a separate thread from the current worker if the fork-stmt occurs in an isolated function.
Variables and parameters in scope for the fork-stmt remain in scope within the workers (similar to the situation with parameters and workers in a function body).
The scope of the worker-name in a named-worker-decl that is part of a fork-stmt consists of both other workers in the same fork-stmt and the block containing the fork-stmt starting from the point immediately after the fork-stmt. Within its scope, the worker-name can be used in a variable-reference-expr in the same way as the worker-name of a named-worker-decl that is part of a block-function-body.
start-action := [annots] start
call-action-or-expr
call-action-or-expr :=
function-call-expr
| method-call-expr
| client-remote-method-call-action
| client-resource-access-action
The keyword start
causes the following function or method
invocation to be executed on a new strand. The arguments for the function or
method call are evaluation on the current strand. A start-action returns a value
of basic type future immediately. If the static type of the call expression or
action C
is T, then the static type of start
C
is future<T>; it is permitted for T to be
never
.
A call-action-or-expr is isolated if the function or method it calls has a type that is isolated and the expression for every argument is an isolated expression; for this purpose, the object whose method is called by the method-call-expr or client-remote-method-call-action is treated as an argument. When a start-action has a call-action-or-expr that is isolated then
A wait-action waits for one or more strands to terminate, and gives access to their termination values.
wait-action := single-wait-action | multiple-wait-action | alternate-wait-action wait-future-expr := expression but not mapping-constructor-expr
A wait-future-expr is used by a wait-action to refer to the worker to be waited for. Its static type must be future<T> for some T. As elsewhere, a wait-future-expr can use an in-scope worker-name in a variable-reference-expr to refer to named worker.
Evaluation of a wait-action performs a wait operation on the future value that results from evaluating a wait-future-expr. This wait operation may complete normally or abruptly. The wait operation initiates a wait for the strand that the future refers to. If the wait fails, then the wait operation completes normally and the result is an error. If the wait succeeds, and the strand completed normally, then the wait operation completes normally, and the result is the termination value of the strand. If the wait succeeds, but the strand completed abnormally, then the wait operation completes abruptly with a panic and the associated value is the termination value of the strand.
In addition to a static type, a wait-future-expr has a compile-time eventual type. If a wait-future-expr is a variable-reference-expr referring to the worker-name of a named worker with return type T, then the eventual type of the wait-future-expr is T. Otherwise, the eventual of a wait-future-expr with static type future<T> is T|error. The result of a wait operation that completes normally will belong to the eventual type of the wait-future-expr, since the compiler ensures that a wait for a wait-future-expr that is a variable reference to a named worker cannot fail.
Note that it is only possible to wait for a named worker, which will start its own strand. It is not possible to wait for a function's default worker, which runs on an existing strand.
A mapping-constructor-expr is not recognized as a wait-future-expr (it would not type-check in any case).
single-wait-action := wait
wait-future-expr
A single-wait-action waits for a single future.
A single-wait-action is evaluated by first evaluating wait-future-expr resulting in a value f of basic type future; the single-wait-action then performs a wait operation on f.
The static type of the single-wait-action is the eventual type of the wait-future-expr.
multiple-wait-action :=wait
{
wait-field (,
wait-field)*}
wait-field := variable-name | field-name:
wait-future-expr
A multiple-wait-action waits for multiple futures, returning the result as a record.
A wait-field that is a variable-name v
is equivalent to a
wait-field v: v
, where v
must be the name of an
in-scope named worker.
A multiple-wait-action is evaluated by evaluating each wait-future-expr resulting in a value of type future for each wait-field. The multiple-wait-action then performs a wait operation on all of these futures. If all the wait operations complete normally, then it constructs a record with a field for each wait-field, whose name is the field-name and whose value is the result of the wait operation. If any of the wait operations complete abruptly, then the multiple-wait-action completes abruptly.
The static type of the multiple-wait-action is a record where the static type of each field is the eventual type of the corresponding wait-future-expr.
alternate-wait-action :=wait
wait-future-expr (|
wait-future-expr)+
An alternate-wait-action waits for one of multiple futures to terminate.
An alternate-wait-action is evaluated by first evaluating each wait-future-expr, resulting in a set of future values. The alternate-wait-action then performs a wait operation on all of these members of this set. As soon as one of the wait operations completes normally with a non-error value v, the alternate-wait-action completes normally with result v. If all of the wait operations complete normally with an error, then it completes normally with result e, where e is the result of the last wait operation to complete. If any of the wait operations completely abruptly before the alternate-wait-action completes, then the alternate-wait-action completes abruptly.
The static type of the alternate-wait-action is the union of the eventual type of all of its wait-future-exprs.
Messages can be sent between workers.
Sends and receives are matched up at compile-time. This allows the connection between the send and the receive to be shown in the sequence diagram. It is also guarantees that any sent message will be received, provided that neither the sending nor the receiving worker terminate abnormally or with an error.
Messages can only be sent between workers that are peers of each other. A function's default worker and its named workers are peers of each other. The workers created in a fork-stmt are also peers of each other. The workers created in a fork-stmt are not peers of a function's default worker nor of its named workers.
peer-worker := worker-name | function
A worker-name refers to a worker named in a named-worker-decl, which must be in
scope; function
refers to the function's default worker. The
referenced worker must be a peer worker.
Each worker maintains a separate logical queue for each peer worker to which it sends messages; a sending worker sends a message by adding it to the queue; a receiving worker receives a message by removing it from the sending worker's queue for that worker; messages are removed in the order in which they were added to the queue.
send-action := sync-send-action | async-send-action sync-send-action := expression->>
peer-worker async-send-action := expression->
peer-worker;
A send-action sends a message to the worker that is identified by peer-worker. A
send-action starts by evaluating the expression, resulting in a value
v
; the Clone abstract operation is then applied to
v
resulting in value c
. This
value c
is added to the message queue maintained by the
sending worker for messages to be sent to the receiving worker; this queue
continues to exist even if the receiving worker has already terminated.
For each send-action S, the compiler determines a unique corresponding receive-action R, such that a message sent by S will be received by R, unless R's worker has terminated abnormally or with failure. It is a compile-time error if this cannot be done. The compiler determines a failure type for the corresponding receive-action. If the receive-action was not executed and its worker terminated normally, then the termination value of the worker will belong to the failure type. The failure type will be a (possibly empty) subtype of error.
The difference between async-send-action and sync-send-action is in what happens
after the message is added to the queue. The evaluation of async-send-action
completes immediately after this, and the result is always ()
. A
subsequent flush action can be used to check whether the message was received.
With sync-send-action, evaluation waits until the receiving worker either
executes a receive action that receives the queued message or terminates. The
evaluation of sync-send-action completes as follows:
The static type of the sync-send-action is F|() where F is the failure type of the corresponding receive action. If F is empty, then this static type will be equivalent to ().
The static type of the expression
must be a subtype of
value:Cloneable
. The contextually expected type used to interpret
expression
is the contextually expected type from the corresponding
receive-action.
If the receive-action corresponding to an async-send-action has a non-empty failure type, then it is a compile-time error unless it can be determined that a sync-send-action or a flush-action will be executed before the sending worker terminates with success.
If a worker W is about to terminate normally and there are messages still to be sent in a queue (which must be the result of executing an async-send-action), then the worker waits until the messages have been received or some receiving worker terminates. If a receiving worker R terminates without the message being received, R must have terminated abnormally, because the rule in the preceding paragraph. In this case, W terminates abnormally and W will use R's termination value as its termination value.
receive-action := single-receive-action | multiple-receive-action
single-receive-action := <-
peer-worker
A single-receive-action receives a message from a single worker.
For each single-receive-action R receiving from worker W, the compiler determines a corresponding send set. The corresponding send set S is a set of send actions in W, such that
The compiler terminates a failure type for the corresponding send set. If no member of the corresponding send set was evaluated and the sending worker terminated normally, then the termination value of the sending worker will belong to the failure type. The failure type will be a (possibly empty) subtype of error.
A single-receive-action is evaluated by waiting until there is a message available in the queue or the sending worker terminates. The evaluation of single-receive-action completes as follows:
The static type of the single-receive-action is T|F where T is the union of the static type of the expressions in the corresponding send set and F is the failure type of the corresponding send set.
multiple-receive-action :=<-
{
receive-field (,
receive-field)*}
receive-field := peer-worker | field-name:
peer-worker
A multiple-receive-action receives a message from multiple workers.
A peer-worker can occur at most once in a multiple-receive-action.
A receive-field consisting of a peer-worker W
is equivalent to a
field W:W
.
The compiler determines a corresponding send set for each receive field, in the same way as for a single-receive-action. A multiple-receive-action is evaluated by waiting until there is a message available in the queue for every peer-worker. If any of the peer workers W terminate before a message becomes available, then the evaluation of the multiple-receive-action completes as follows
Otherwise, the result of the evaluation of multiple-receive-action completes by removing the first message from each queue and constructing a record with one field for each receive-field, where the value of the record is the message received.
The contextually expected typed for the multiple-receive-action determines a contextually expected type for each receive-field, in the same way as for a mapping constructor. The contextually expected type for each receive-field provides the contextually expected type for the expression in each member of the corresponding send set.
The static type of multiple-receive-action is R|F where
flush-action := flush
[peer-worker]
If peer-worker is specified, then flush waits until the queue of messages to be received by peer-worker is empty or until the peer-worker terminates.
Send-receive correspondence for async-send-action implies that the queue will eventually become empty, unless the peer-worker terminates abnormally or with failure. The evaluation of flush-action completes as follows:
If the flush-action has a preceding async-send-action without any intervening sync-send-action or other flush-action, then the static type of the flush-action is F|(), where F is the failure type of the receive-action corresponding to that async-send-action. Otherwise, the static type of the flush-action is nil.
If peer-worker is omitted, then the flush-action flushes the queues to all other workers. In this case, the static type will be the union of the static type of flush on each worker separately.
This section provides further details about how compile-time correspondence is established between sends and receive. This is based on the concept of the index of a message in its queue: a message has index n in its queue if it is the nth message added to the queue during the current execution of the worker.
A client object is a stub that allows a worker to send network messages to a remote system according to some protocol. A local variable declared with client object type will be depicted as a lifeline in the function's sequence diagram.
The remote methods of the client object correspond to distinct network messages defined by the protocol for the role played by the client object. The return value of a remote method represents the protocol's response. A client-remote-method-call-action is depicted as a horizontal arrow from the worker lifeline to the client object lifeline.
client-remote-method-call-action := expression->
remote-method-name(
arg-list)
Calls a remote method of a client object. This works the same as a method call
expression, except that it is used for a client object method with the
remote
qualifier.
client-resource-access-action := expression->
/
[resource-access-path] [.
resource-method-name] [(
arg-list)
] resource-access-path := resource-access-path-segments [/
resource-access-rest-segment] | resource-access-rest-segment resource-access-path-segments := resource-access-path-segment (/
resource-access-path-segment )* resource-access-path-segment := resource-path-segment-name | computed-resource-access-path-segment computed-resource-access-path-segment :=[
expression]
resource-access-rest-segment :=[
...
expression]
A client-resource-access-action
performs a resource access operation on a client object. An
omitted resource-method-name
defaults to get
; if the
parentheses and the arg-list
are omitted, then they default to
()
, with arguments being defaulted as usual.
A client-resouce-access-action
is evaluated by:
->
resulting in
a client object c;resource-access-path
to get a list of values
p
resource-access-path
that is just
makes p be
an empty listresource-path-segment-name
appends a string to p
computed-resource-access-path-segment
is evaluated and the
result is appended to p;resource-access-rest-segment
is evaluated and every member
of the result, which must be a list, is appended to p;arg-list
to get an argument list
a;resource-method-name
, p and a, resulting in a value
v.
The result of the client-resource-access-action
is v.
The static type of the client object must contain a resource with specified
method name for every list value that may be constructed by the evaluation of
the resource-access-path
. The argument list and result value are
typed using the intersection of the function types allowed for the method name
with any of the resource paths.
query-action := query-action-pipeline do-clause
do-clause := do
statement-block
The clauses in the query-pipeline of query-action are executed in the same way as the clauses in the query-pipeline of a query-expr.
The query-action is executed as follows. For each input frame f emitted by the query-pipeline, execute the statement-block with f in scope. If a clause in the query-pipeline completes early with error e, the result of the query-action is e. Otherwise, the result of the query-action is nil.
local-var-decl-stmt := local-init-var-decl-stmt | local-no-init-var-decl-stmt local-init-var-decl-stmt := [annots] [final
] typed-binding-pattern=
action-or-expr;
A local-var-decl-stmt
is used to declare variables with a scope
that is local to the block in which they occur.
The scope of variables declared in a local-var-decl-stmt
starts
immediately after the statement and continues to the end of the statement block
in which it occurs.
A local-init-var-decl-stmt is executed by evaluating the action-or-expr
resulting in a value, and then matching the typed-binding-pattern to the value,
causing assignments to the variables occurring in the typed-binding-pattern. The
typed-binding-pattern is used unconditionally, meaning that it is a compile
error if the static types do not guarantee the success of the match. If the
typed-binding-pattern uses var
, then the type of the variables is
inferred from the static type of the action-or-expr; if the
local-init-var-decl-stmt includes final, the precise type is used, and otherwise
the broad type is used. If the typed-binding-pattern specifies a
type-descriptor, then that type-descriptor provides the contextually expected
type for action-or-expr.
If final
is specified, then the variables declared must not be
assigned to outside the local-init-var-decl-stmt.
local-no-init-var-decl-stmt := [annots] [final
] type-descriptor variable-name;
A local variable declared local-no-init-var-decl-stmt
must be
definitely assigned at each point that the variable is referenced. This means
that the compiler must be able to verify that the local variable will have been
assigned before that point. If final
is specified, then the
variable must not be assigned more than once. The type-descriptor
must not be never
.
Usually the type of a reference to a variable is determined by the variable's declaration, either explicitly specified by a type descriptor or inferred from the static type of the initializer. However, the language also recognizes two kinds of case where the way a local variable or parameter is used means that is known at compile-time that within a particular region of code the value of the variable will belong to a type that is narrower than the declared type. In these cases, references to the variable within particular regions of code will have a static type that is narrower that the variable type. One kind of case, which is described in this section, is when a variable is used in a boolean expression in a conditional context. This is sometimes called occurrence typing. The other kind of case is when a variable is used in a match statement; this is described in the Match statement section.
Type narrowing makes use of the read-only difference between two types, which is defined as follows. We use the notation T −ro S for the read-only difference of T and S. T −ro S is computed separately for each uniform type. Let TU be the subset of T belonging to uniform type U, i.e. the intersection of T and U. For a uniform type U that is immutable, TU −ro SU is the same as TU - SU (set difference); for a uniform type U that is mutable, TU −ro SU is empty if TU is a subset of SU, and TU otherwise. T −ro S is the union for every uniform type U of TU −ro SU. This definition of read-only difference ensures that if a value x belongs to T but not does not belong to S, then x belongs to T −ro S.
Given an expression E with static type boolean, and a variable x with static type Tx, we define how to determine
based on the syntactic form of E as follows.
is
T, then
==
E1 or E1
==
x where the static type of E1 is an expression whose
static type is a singleton simple type T1, then
!=
E1 or E1
!=
x where the static type of E1 is an expression whose
static type is a singleton simple type T1, then
!
E1, then
&&
E2
||
E2, then
Narrowed types implied by the truth or falsity of an expression apply to regions of code as follows:
||
E2, the narrowed
types implied by the falsity of E1 apply within E2
&&
E2, the
narrowed types implied by the truth of E1 apply within
E2
?
E2 :
E3, the narrowed types implied by the truth of E1 apply
within E2 and the narrowed types implied by the falsity of
E1 apply within E3
if
E1 B1 else
B2, the narrowed types implied by the truth of E1 apply
within B1 and the narrowed types implied by the falsity of
E1 apply within B2
while
E B, the narrowed types implied by the truth of E
apply with Bif
E =>
B, the narrowed types
implied by the truth of E apply within BGiven a statement or statement-block S, and a variable x that is in-scope for S and has static type Tx, we define how to determine a narrowed type for x implied by the normal completion of S as follows.
{
S1 S2 ...
Sn }
, then the narrowed type for x implied by the normal
completion of S is the narrowed type for x implied by the normal completion of
Sn.do
B, the narrowed type for x implied by the
normal completion of S is the narrowed type for x implied by the normal
completion of B.if
E B1 else
B2, the narrowed type for x implied by the normal completion of S is
the union of T1 and T2, where T1 is the
narrowed type for x implied by the normal completion of B1 and
T2 is the narrowed type for x implied by the normal completion of
B2. (The truth or falsity of E may imply a narrowed type for x that
applies in B1 or B2, which may affect the narrowed type
for x implied by the normal completion of B1 or B2.)
The narrowed types implied by the normal completion of a statement are applied
within a statement-block as follows: in a statement-block {
S1 S2 ... Sn }
, for each i in 2,..., n, the
narrowed types implied by the normal completion of Si - 1 apply to
Si.
In the above rules, a if-else-stmt without an else
clause is
treated as if it had an else
followed by an empty statement-block,
and else if
clause is treated like a nested if-else-stmt.
A narrowed type for a variable x implied by an expression in which x occurs ceases to apply in a region of code where there is a possibility at runtime that x has been assigned to since the evaluation of the expression. It is an error if this results in the type of a variable reference being affected by assignments that lexically follow the variable reference. Specifically, when a variable x is narrowed outside a while-stmt, foreach-stmt or query-action S, an assignment to x within S must not result in the possibility of x having being assigned to when the statement-block of S completes normally or when a continue-stmt is executed.
xmlns-decl-stmt := xmlns-decl xmlns-decl :=xmlns
xml-namespace-uri [as
xml-namespace-prefix ];
xml-namespace-uri := simple-const-expr xml-namespace-prefix := identifier
An xmlns-decl
is used to declare a XML namespace. If there is
an xml-namespace-prefix
, then the in-scope namespaces that are used
to perform namespace processing on an xml-template-expr
will include a
binding of that prefix to the specified xml-namespace-uri
;
otherwise the in-scope namespaces will include a default namespace with the
specified xml-namespace-uri
.
An xml-namespace-prefix
declared by an xmlns-decl
is in the same symbol space as a module-prefix
declared by an
import-decl
. This symbol space is distinct from a module's main
symbol space used by other declarations. An xmlns-decl
in a
xmlns-decl-stmt
declares the prefix within the scope of the current
block.
The prefix xmlns
is predeclared as
http://www.w3.org/2000/xmlns/
and cannot be redeclared.
The static type of the simple-const-expr
must be a subtype of
string.
There are three kinds of assignment statement:
The first two of these build on the concept of an lvalue, whereas the last one builds on the concept of a binding pattern.
An lvalue is what the left hand side of an assignment evaluates to. An lvalue refers to a storage location which is one of the following:
Note that an lvalue cannot refer to a member of a table.
An lvalue that is both defined and initialized refers to a storage location that holds a value:
init
method returnslvexpr := variable-reference-lvexpr | field-access-lvexpr | member-access-lvexpr
The left hand side of an assignment is syntactically a restricted type of expression, called an lvexpr. When the evaluation of an lvexpr completes normally, its result is an lvalue. The evaluation of an lvexpr can also complete abruptly, with a panic or check-fail, just as with the evaluation of an expression.
The compiler determines a static type for each lvexpr just as it does for expressions, but the meaning is slightly different. For an lvexpr L to have static type T means that if the runtime evaluation of L completes normally resulting in an lvalue x, then if x is defined and initialized, it refers to a storage location that holds a value belonging to type T. In addition to a type, the compiler determines for each lvexpr whether it is potentially undefined and whether it is potentially uninitialized.
An lvalue supports three operations: store, read and filling-read.
The fundamental operation on an lvalue is to store a value in the storage location it refers to. This operation does not required the lvalue to be defined or initialized; a successful store operation on an undefined lvalue will result in the addition of a member to the structured value; a store on an uninitialized lvalue will initialize it. When an lvalue refers to a variable, it is possible to determine at compile-time whether the store of a value is permissible based on the declaration of the variable and the static type of the value to be stored. However, when the lvalue refers to a member of a structured value, this is not in general possible for three reasons.
The first of these reasons also applies to lvalues that refer to fields of objects. Accordingly, stores to lvalues other than lvalues that refer to variables must be verified at runtime to ensure that they are not impermissible for any of the above three reasons. An attempt to perform an impermissible store results in a panic at runtime.
List values maintain the invariant that there is a unique integer n, the length of the list, such that a member k of the list is defined if and only if k is a non-negative integer less than n. When a store is performed on an lvalue referring to a member k of a list value and the length of the list is n and k is > n, then each member with index i for each ≤ i < k is filled in, by using the FillMember abstract operation. The FillMember abstract operation may fail; in particular it will fail if the list is a fixed-length array. If the FillMember operation fails, then the attempt to store will panic.
An lvalue also allows a read operation, which is used by the compound assignment statement. Unlike a store operation, a read operation cannot result in a runtime panic. A read operation cannot be performed on an lvalue that is undefined or uninitialized.
Finally, a lvalue supports a filling-read operation, which is used to support chained assignment. A filling-read is performed only an lvalue with a static type that is a structured type. It differs from a read operation only when it is performed on a potentially undefined lvalue. If the lvalue is undefined at runtime, then the filling-read will use the FillMember abstract operation on the member that the lvalue refers to. If the FillMember operation fails, then the filling-read panics. Unlike the read operation, the filling-read operation can be performed on an undefined lvalue; it cannot, however, be performed on an uninitialized lvalue.
The evaluation of an lvexpr is specified in terms of the evaluation of its subexpressions, the evaluation of its sub-lvexprs, and store and filling-read operations on lvalues. If any of these result in a panic, then the evaluation of the lvexpr completes abruptly with a panic.
variable-reference-lvexpr := variable-reference
The result of evaluating variable-reference-lvexpr is an lvalue referring to a variable. Its static type is the declared or inferred type of the variable. The lvexpr is potentially uninitialized if it is possible for execution to have reached this point without initializing the referenced variable.
A variable-reference-lvexpr that refers to a variable declared by a
module-var-decl that includes isolated
is only allowed within a
lock-stmt.
field-access-lvexpr := lvexpr .
field-name
The static type of lvexpr must be either a subtype of the mapping basic type or a subtype of the object basic type.
In the case where the static type of lvexpr is a subtype of the object basic type, the object type must have a field with the specified name, and the resulting lvalue refers to that object field.
In the case where the static type of lvexpr is a subtype of the mapping basic type, the semantics are as follows.
field-name
as an individual-field-descriptor (if
lvexpr is a union, then this requirement applies to every member of the union);
lvexpr must not be potentially uninitialized, but may be potentially
undefined.member-access-lvexpr := lvexpr[
expression]
The static type of lvexpr must be either a subtype of the mapping basic type or a subtype of the list basic type. In the former case, the contextually expected type of expression must be string and it is an error if the static type of expression is not string; in the latter case, the contextually expected type of expression must be int and it is an error if the static type of expression is not int.
It is evaluated as follows:
The static type of the member-access-lvexpr is the member type for the key type K in T, where T is the static type of the lvexpr and K is the static type type of expression; the member-access-lvexpr is potentially undefined if K is an optional key type for T.
assignment-stmt := lvexpr=
action-or-expr;
The static type of action-or-expr must be a subtype of the static type of lvexpr. The static type of lvexpr provides the contextually expected type for action-or-expr. It is not an error for lvexpr to be potentially undefined or potentially uninitialized.
It is executed at as follows:
compound-assignment-stmt := lvexpr CompoundAssignmentOperator action-or-expr;
CompoundAssignmentOperator := BinaryOperator=
BinaryOperator :=+
|-
|*
|/
|&
||
|^
|<<
|>>
|>>>
It is a compile error if lvexpr is potentially undefined unless the static type of lvexpr is a subtype of the list basic type. It is a compile error if lvexpr is potentially uninitialized.
Let T1 be the static type of lvexpr, and let T2 be the static type of action-expr. Let E be an expression E1 BinaryOperator E2, where E1 has type T1 and E2 has type T2. E must be valid according to the static typing rules applicable to the underlying form of the BinaryOperator (not the lifted form described in the Nil lifting section). Let T be the static type of E according to those rules. It is a compile error if T is not a subtype of T1.
A compound-assignment-stmt is executed as follows:
destructuring-assignment-stmt := binding-pattern-not-variable-reference=
action-or-expr;
binding-pattern-not-variable-reference := wildcard-binding-pattern | list-binding-pattern | mapping-binding-pattern | error-binding-pattern
A destructuring assignment is executed by evaluating the action-or-expr resulting in a value v, and then matching the binding pattern to v, causing assignments to the variables occurring in the binding pattern.
The binding-pattern has a static type implied by the static type of the variables occurring in it. The static type of action-or-expr must be a subtype of this type.
A binding pattern within a destructuring assignment must not refer to a variable
declared by a module-var-decl that includes isolated
.
Note that a destucturing-assignment-stmt with a wildcard-binding-pattern can be used to ensure that a a variable has been narrowed as expected.
type T X|Y|Z; function foo(T v) { if v is X { handleX(v); } else if v is Y { handleY(v); } else { Z _ = v; handleOther(v); } }
If T
is subsequently modified to add another possibility in
addition to X
, Y
and Z
, then
foo
will get a compile error.
action-stmt := action ;
An action-stmt is a statement that is executed by evaluating an action and discarding the resulting value, which must be nil. It is a compile-time error if the static type of an action in an action-stmt is not nil.
call-stmt := call-expr ;
call-expr := function-call-expr | method-call-expr
A call-stmt is executed by evaluating call-expr as an expression and discarding
the resulting value, which must be nil. The static type of the call-expr in a
call-stmt must be either ()
or never
. If it is
never
, then it is possible for the call-stmt to complete normally.
checking-stmt := checking-action-or-expr ;
A checking-stmt is executed by evaluating checking-action-or-expr as an
expression and discarding the resulting value, which must be nil. The static
type of the checking-action-or-expr in a checking-stmt must be ()
.
if-else-stmt :=if
expression statement-block [else
if
expression statement-block ]* [else
statement-block ]
The if-else statement is used for conditional execution.
The static type of the expression following if
must be boolean.
When an expression is true then the corresponding statement block is executed
and the if statement completes. If no expression is true then, if the else block
is present, the corresponding statement block is executed.
do-stmt := do
statement-block
A do-stmt
is executed by executing its
statement-block
.
match-stmt :=match
match-target{
match-clause+}
match-target := action-or-expr match-clause := match-pattern-list [match-guard]=>
statement-block match-guard :=if
match-guard-expr
A match statement selects a statement block to execute based on which patterns a value matches.
A match-stmt is executed as follows:
The scope of any variables created in a match-pattern-list of a match-clause is both the match-guard, if any, and the statement-block in that match-clause. The static type of the expression in match-guard must be a subtype of boolean.
match-guard-expr := expression
A match-guard-expr must not include a function-call-expr
or
method-call-expr
unless either
action-or-expr
following
match
is a subtype of readonly
; orisolated
and the static
type of every argument to the function or method is a subtype of
readonly
; in the case of a method-call-expr, the static type of the
object whose method is being called must also be a subtype of
readonly
.match-pattern-list :=
match-pattern (|
match-pattern)*
A match-pattern-list can be matched against a value. An attempted match can succeed or fail. A match-pattern-list is matched against a value by attempting to match each match-pattern until a match succeeds.
All the match-patterns in a given match-pattern-list must bind the same set of variables.
match-pattern :=
var
binding-pattern
| wildcard-match-pattern
| const-pattern
| list-match-pattern
| mapping-match-pattern
| error-match-pattern
A match-pattern combines the destructuring done by a binding-pattern with the ability to match a constant value.
Note that an identifier can be interpreted in two different ways within a match-pattern:
var
, an identifier names a variable which is
to be bound to a part of the matched value when a pattern match succeeds;A match-pattern must be linear: a variable that is to be bound cannot occur more than once in a match-pattern.
const-pattern := simple-const-expr
A const-pattern denotes a single value. Matching a const-pattern denoting a value p against a value v succeeds if DeepEquals(p,v) is true. The static type of the simple-const-expr in a const-pattern must be a subtype of anydata. Successfully matching a const-pattern does not cause any variables to be created.
Matching a wildcard-match-pattern against a value succeeds if the value belongs to type any, in other words if the basic type of the value is not error.
wildcard-match-pattern :=_
list-match-pattern :=[
list-member-match-patterns]
list-member-match-patterns := match-pattern (,
match-pattern)* [,
rest-match-pattern] | [ rest-match-pattern ] mapping-match-pattern :={
field-match-patterns}
field-match-patterns := field-match-pattern (,
field-match-pattern)* [,
rest-match-pattern] | [ rest-match-pattern ] field-match-pattern := field-name:
match-pattern rest-match-pattern :=...
var
variable-name error-match-pattern :=error
[error-type-reference](
error-arg-list-match-pattern)
error-arg-list-match-pattern := error-message-match-pattern [,
error-cause-match-pattern] [,
error-field-match-patterns] | [error-field-match-patterns] error-message-match-pattern := simple-match-pattern error-cause-match-pattern := simple-match-pattern | error-match-pattern simple-match-pattern := wildcard-match-pattern | const-pattern |var
variable-name error-field-match-patterns := named-arg-match-pattern (,
named-arg-match-pattern)* [,
rest-match-pattern] | rest-match-pattern named-arg-match-pattern := arg-name=
match-pattern
Matching a mapping-match-pattern
against a mapping value succeeds
if and only every field-match-pattern
matches against a field of
the value. The variable in the rest-match-pattern
, if specified, is