Basic Constraints¶
As mentioned in the Language Concepts section, the core idea of Statix is to see a type-checking problem as a constraint solving problem. Therefore, it is crucial to be able to express constraints in a specification.
In this section, we discuss all constraints that are not related to scope graphs. These constraints are explained in-depth in the sections on Scope Graph Construction and Queries.
True¶
true
The true
constraint is the constraint that is trivially satisfied.
False¶
false $Message?
The false
constraint is the constraint that will always fail. Just as all other
constraints that can fail, it is possible to add a message.
Conjunction¶
$Constraint,
$Constraint
A conjunction of constraints is satisfied when both conjuncts are satisfied. Note that the solving order of the conjuncts is undefined.
Equality¶
$Term == $Term $Message?
Asserts that two terms are equal. When necessary, this constraint infers values for free variables in the terms. Statically, both terms should have the same type.
Disequality¶
$Term != $Term $Message?
Asserts that two terms are not equal. Statically, both terms should have the same type.
Statix treats free variables as different from each other, and as different from concrete terms. Therefore, when any of both terms is not ground (i.e. contains free variables), the constraint will not fail.
Exists¶
{ $Var* }
$Constraint
An exists constraint introduces new existentially quantified variables in the
scope of its subconstraint. The names of the variables in an exists constraint
should be unique (i.e. {x x} true
is not allowed), but are allowed to shadow
outer variables.
Try¶
try { $Constraint } $Message?
The try
constraint validates whether the outer context implies that the inner
constraint holds. That is: any model for the outer context (all other constraints
than $Constraint
) is also a (not necessarily minimal) model for $Constraint
.
In order to implement these semantics, the $Constraint
is handled differently
than regular constraints in two ways.
- The
$Constraint
is not allowed to refine the outer context. Therefore, equality constraints will not infer values for variable that were introduced outside thetry
. Likewise, scopes can not be instantiated, nor edges/declarations added to scopes from outside thetry
. This behavior also implies that values constructed within atry
construct will never escape thetry
context. - Disequalities in
$Constraint
that involve free variables cause thetry
to fail, because appearently the disequality does not hold for all models of the outer context.
Todo
Explain the design choices for try
in a background section.
AST Identifiers¶
astId($Term, $Term)
The astId
constraint asserts that its second argument is the term index of the
first argument. The type of the first argument may be anything, but the type of
the second argument is astId
.
Tip
Often, using an astId
term will read more natural.
AST Property¶
@$Term.$Prop $Op $Term
Statix allows to set properties on AST nodes. These properties can be used to communicate typing results to the outside world, for example to be used in a transformation. For more information on reading these properties, please refer to the Stratego API documentation.
The first term is the term (usually an AST node) on which the property is set.
Next $Prop
specifies which property is set. This property can be any string
of the form [a-zA-Z] [a-zA-Z0-9\_]*
. It is not required to declare properties.
There are two special properties: ref
and type
. ref
properties are set on
(syntactic) variable references, and point to the term they are referencing. The
Spoofax reference resolution service uses these properties to offer reference
resolution in the editor. The type
property contains the type of a term. This
type is shown when hovering over a term in an editor.
The $Op
specifies the operator with which a property is set. There are two
possible operators:
:=
: The assignment operator. This operator requires a property to have only a single unique value (although, since Spoofax 2.5.17, that value may be set multiple times).+=
: The bag insertion operator. Properties using this operator can be set multiple times, and will be aggregated in the eventual property value.
Note that using both operators for a single property on a particular node will result in a failed constraint. However, it is allowed to use different operators for a property, as long as the terms on which these operators are used are different.
Finally, the last $Term
denotes the value of the property.
Warning
Failing property constraints are ignored (i.e., no error for them is reported).
Arithmetic Constraints¶
$Term $Op $ArithExp $Message?
Statix supports several arithmetic constraints. These constraints consist of a
term, an operator and an arithmetic expression. The $Term
should have type int
,
while the $ArithExp
is syntactically guaranteed to have type int
, given that
all variable references have type int
.
At the $Op
position, several comparison operators can be used:
#=
asserts that both terms are equal#\\=
asserts that both terms are not equal#>=
asserts that the left term is equal or bigger that the left term#=<
asserts that the left term is equal or smaller that the left term#>
asserts that the left term is strictly bigger that the left term#<
asserts that the left term is strictly smaller that the left term
Arithmetic expressions can be integer literals, variables and bracketed arithmetic
expressions. Variables in an arithmetic expression must have type int
.
Additionally, the following arithmetic operations can be used:
$ArithExp + $ArithExp
: computes integer addition$ArithExp - $ArithExp
: computes integer subtraction$ArithExp * $ArithExp
: computes integer multiplicationmin($ArithExp, $ArithExp)
: computes the minimum of both argumentsmax($ArithExp, $ArithExp)
: computes the maximum of both arguments$ArithExp div $ArithExp
: computes the integer divisor (i.e. rounded down regular division).$ArithExp mod $ArithExp
: computes the modulus of its arguments.
Unlike other constraints, arithmetic constraint do no inference of values in
the $ArithExp
. Hence, having any free variables in this expression will cause
the constraint to fail.
Separate Arithmetic Expression Syntax
As discussed, Statix has a special syntactic category for arithmetic expressions. Therefore, arithmetic expressions cannot be used at regular term positions. Instead, arithmetic expressions can be embedded in terms using the Arithmetic Expressions term syntax.
Java Integers
Arithmetic Expressions are implemented using standard Java integers, and hence have the same size limitations.
Messages¶
| $Severity $MessageBody $Position?
Constraints that might possibly fail can be provided with a customized message. Such message carry three parameters.
First, the $Severity
indicates the severity of a message. It may be either
error
, warning
or note
. Note however that warnings and notes can only be
issued for failing try
constraints.
Second, the error message string is provided. This message string may be a regular string literal or a template literal. Template literals look as follows:
$[$ContentPart*]
Content parts are either message string literals or interpolated terms. The
escaping rules for string literals in templates are slightly different than for
regular string literals. Message string literals can contain any character,
where square brackets and backslashes must be escaped with a backslash. Just as
regular string literals, tabs, newlines and carriage returns can be encoded with
\t
, \n
and \r
, respectively.
Terms can be inserted in a message template by surrounding them with
(unescaped) square brackets: [$Term]
. The term may have any type, but must be
well-formed according to the regular typing rules for terms.
Bug
Using functional predicates inside message templates will cause an exception when loading the specification.
Thirdly, a position can be assigned to the message:
@ $Var
Here, the $Var
is assumed to be an AST node. When the constraint on which this
message is placed fails, the message will be shown inline at the AST node pointed
to by this variable.
When no message position is provided, or the assigned position is invalid, Statix will scan the the arguments to user-defined constraints on the call trace that led to the failed constraint from left to right for an AST argument, and put the message on the first valid node found. When no AST node could be found (for example when the project constraint fails), the error is positioned at the project resource.
Warning
Messages on projects are often overlooked by users. Hence it is recommended that project constraints are designed in a way they can never fail.
When message is provided for a failed constraint, Statix will scan the call trace for constraints that have a message provided, and use the message it encounters. If no message is found, a rendering of the failed constraint is used as a message.
Created: October 17, 2024