Type Checking
This simple Python script shows off SysIDE’s type checking capabilities. In the provided example model, there are six packages:
Non-Conforming Types 1
Non-Conforming Types 2
Conforming Types 1
Collect
Select
Arrays
In the first package, the a
attribute is a Boolean
and the b
attribute is
defined to be a String
. However, we are assigning a
to b
, which is not
allowed because Boolean
does not conform to String
.
In the second package, the b
attribute is defined to be a Positive
. However, we
are assigning a value of -42
to it, which is inferred to be an Integer
and thus
is not conformant to Positive
. The a
attribute is defined and assigned
correctly.
The third package is allowed because both attributes are Positive
and conform to the
ScalarValues::Positive
type.
The fourth package Collect
shows type inference working on standard collect
function. In both cases, the feature values are inferred to return types A
, thus
only attribute b
is diagnosed with an invalid type.
Similarly to the previous package, Select
shows type inference correctly identifying
that the expression of a
selects only Positive
values, and thus it is not an
error to assign the result to a
. Expression of b
does no such checking,
therefore it if inferred to return Integer
values from the negative range lower
bound which is diagnosed as a type error when being assigned to Positive
.
The last package Arrays
shows inference working on owned argument features, and
collection and array indexing correctly inferring the return type to be
Arrays::array::elements
which conforms to Positive
, hence no type errors. Note
that because operator #
is statically ambiguous, this inference only works by
explicitly calling CollectionFunctions::'#'
and CollectionFunctions::'array#'
functions. The ambiguity stems from the operator handling single ordered collections and
arrays differently to all other sequences, of which the multiplicity cannot be
statically known in all cases since everything is both a sequence and a single value at
the same time in SysML v2.
Most other standard library functions that are definitions of operator functions, or operate on sequences, are also treated as special functions by the type inference to reduce the boilerplate needed to satisfy the type checker.
Note
Before running this example, make sure you have activated the SysIDE license by
running syside-license check
according to the instructions in the
License Activation section.
Concepts Used
This example only uses the load_model
function.
Example Model
package 'Non-Conforming Types 1' {
private import ScalarValues::*;
attribute a : Boolean = true;
attribute b : String = a;
}
package 'Non-Conforming Types 2' {
private import ScalarValues::*;
attribute a : Positive = 42;
attribute b : Positive = -42;
}
package 'Conforming Types 1' {
private import ScalarValues::*;
attribute a : Positive = 42 + 0;
attribute b : Positive = a;
}
package Collect {
private import ControlFunctions::collect;
private import ScalarValues::Positive;
attribute def A { attribute value : Positive; }
attribute def B;
attribute a : A [*] = (1..10)->collect { in attribute x : Positive; A(x) };
attribute b : B [*] = (1..10)->collect { in attribute x : Positive; A(x) };
}
package Select {
private import ControlFunctions::select;
private import ScalarValues::*;
attribute a : Positive [*] = (-1..10)->select { in attribute x : Integer; x istype Positive };
attribute b : Positive [*] = (-1..10)->select { true };
}
package Arrays {
private import Collections::Array;
private import ScalarValues::*;
attribute array : Array {
:>> elements : Positive;
}
attribute a : Positive = array->CollectionFunctions::'#'(1);
attribute b : Positive = array->CollectionFunctions::'array#'(1);
}
Example Script
import pathlib
import syside
MODEL = "example_model.sysml"
EXAMPLE_DIR = pathlib.Path(__file__).parent
MODEL_FILE_PATH = EXAMPLE_DIR / MODEL
def main() -> None:
(_, diagnostics) = syside.try_load_model([MODEL_FILE_PATH])
# Convert diagnostics to use relative paths
relative_diagnostics = str(diagnostics).replace(str(EXAMPLE_DIR) + "/", "")
print(relative_diagnostics)
if __name__ == "__main__":
main()
Output
example_model.sysml:5:28: error (type-error): 'Non-Conforming Types 1'::a does not conform to ScalarValues::String
example_model.sysml:12:30: error (type-error): ScalarValues::Integer does not conform to ScalarValues::Positive
example_model.sysml:30:27: error (type-error): Collect::A does not conform to Collect::B
example_model.sysml:38:34: error (type-error): ScalarValues::Integer does not conform to ScalarValues::Positive
Download
Download this example here.