Skip to content

ST6RI-818 Constructor expressions (KERML_-132/222, SYSML2_-766) #638

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Mar 27, 2025

Conversation

seidewitz
Copy link
Member

@seidewitz seidewitz commented Mar 25, 2025

This PR implements resolutions to the following issues, as approved in KerML FTF2 Ballots #5 and #6 and SysML v2 FTF 2 Ballot #8:

  • KERML_-132 Constructor invocation expressions are semantically inconsistent
  • KERML_-222 validateParameterMembershipOwningType will fail for ConstructorExpressions
  • SYSML2_-766 Constructor expressions in SysML

Previously, an invocation expression where the invoked type was not a function acted as a constructor for an instance of the invoked type. The resulting usage was both typed by the invoked type and a subset of Performances::evaluations. But, if the invoked type was a data type or structure, then this was a semantic inconsistency, because Performance (a supertype of evaluations) is disjoint with DataValue and Object. The resolution to KERML_-132 fixes this by introducing a new kind of constructor expression and requiring that the invoked type of an invocation expression be a behavior or function.

Abstract Syntax

  1. InstantiationExpression
    • Added as a abstract superclass of InvocationExpression and ConstructorExpression.
    • Defines a derived property instantiatedType, which is the Type invoked/instantiated by the expression.
    • In order to allow more consistent parsing, the instantiatedType of any kind of InstantiationExpression, other than an OperatorExpression or TriggerInvocationExpression (in SysML), is parsed as a member of the expression. (There are special derivation rules for the instantiatedType of an OperatorExpression or TriggerInvocationExpression.)
    • An InstantiationExpression must own its result parameter.
  2. InvocationExpression
    • An InvocationExpression is required to specialize the instantiatedType, as before.
    • The instantiatedType of an InvocationExpression must be a Behavior, normally a Function.
    • If the instantiatedType is a Behavior that is not a Function, then the InvocationExpression is still typed by the Behavior, with its result bound to the expression itself. This allows non-Function Behaviors to still be essentially "constructed" using an InvocationExpression, as before. (This does not cause semantic problems, because it is acceptable to specialize a Performance of the Behavior to an Evaluation that returns itself.)
    • The arguments of an InvocationExpression are parsed as FeatureValues of ownedFeatures of the expression itself, as before.
  3. ConstructorExpression
    • Added to replace the use of InvocationExpressions with constructor semantics.
    • The result of a ConstructorExpression is required to specialize the instantiatedType.
    • The arguments of a ConstructorExpression are parsed as FeatureValues of ownedFeatures of the result of the ConstructorExpression. (In this way, a value of the instantiatedType is "constructed" as the result of the ConstructorExpression, while the expression itself can still be typed as an Evaluation.)
  4. FeatureReferenceExpression
    • FeatureReferenceExpression is also updated with a new validation constraint requiring that it own its result parameter.

Note. The Pilot Implementation previously inserted an owned result parameter for all kinds of expressions. Now this is only done for InstantiationExpressions and FeatureReferenceExpressions.

Concrete Syntax

The concrete syntax for a constructor expression is similar to that of an invocation expression, but preceded by the keyword new.

class Member {
    feature firstName : String;
    feature lastName : String;
    feature memberNumber : Integer;
    feature sponsor : Member[0..1];
}
feature thisMember = new Member("Jane", "Doe", 1234, null);
feature nextMember = new Member(
    firstName = "John", lastName = "Doe", sponsor = thisMember,
    memberNumber = thisMember.memberNumber + 1);

Model Libraries

Kernel Semantic Library

  1. Performances – Add constructorEvaluations as the base feature of all ConstructorExpressions. constructorEvaluations is a specialization of evaluations that restricts the multiplicity of its result parameter to 1..1, requiring a constructorEvaluation to result in a single value.
  2. Triggers – Use the new syntax to construct the result values for TriggerWhen and TriggerAt.

SysML Domain Libraries

  1. Analysis Domain Library, SampleFunctions – Use the new syntax to construct the result value for the calculation definition Sample.
  2. Metadata Domain Library, RiskMetadata – Use the new syntax to construct the enumerated values of the enumeration definition RiskLevel.
  3. Quantities and Units Library, MeasurementReferences – Use the new syntax to construct the value of the attribute usage one.

- Removed this from FunctionAdapter and ExpressionAdapter.
- Added it back just in FeatureReferenceExpressionAdapter and
InvocationExpressionAdapter.
- Derivations and operations for invocation and constructor expressions.
- instantiatedType operation for operator and trigger invocation
expressions.
validateFeatureReferenceResult
validateConstructorExpressionNoDuplicateFeatureRedfinition
validateConstructorExpressionOwnedFeatures
validateInstantiationExpressionInstantiatedType
validateInstantiationExpressionResult
validateInvocationExpressionInstantiatedType
validateInvocationExpressionNoDuplicateParameterRedefinition (updated)
validateInvocationExpressionOwnedFeatures
validateInvocationExpressionParameterRedefinition (updated)
@seidewitz seidewitz added this to the 2025-02 milestone Mar 25, 2025
@seidewitz seidewitz self-assigned this Mar 25, 2025
@seidewitz seidewitz requested review from hpdekoning and himi March 25, 2025 05:09
@himi
Copy link
Member

himi commented Mar 26, 2025

I am reviewing this. I could not find any issues so far. But I found ConstructorExpression can be used for behavior as well as InvocationExpression.

For example,

behavior B1 {
   feature f0;
}
feature f1 = B1();
feature f2 = new B1();

Are these expressions having the same semantics?

P.S.
I confirmed InvocationExpression could not be used for instantiating non-behavior Classes.

@seidewitz
Copy link
Member Author

For example,

behavior B1 {
   feature f0;
}
feature f1 = B1();
feature f2 = new B1();

Are these expressions having the same semantics?

Yes, this is mentioned in the PR description:

  1. InvocationExpression
    ...

    • If the instantiatedType is a Behavior that is not a Function, then the InvocationExpression is still typed by the Behavior, with its result bound to the expression itself. This allows non-Function Behaviors to still be essentially "constructed" using an InvocationExpression, as before. (This does not cause semantic problems, because it is acceptable to specialize a Performance of the Behavior to an Evaluation that returns itself.)

    ...

@hpdekoning
Copy link
Member

👍 Good for merge. I did not find any overlooked constructor expresssions that need a new keyword.

@seidewitz seidewitz changed the base branch from ST6RI-815 to master March 26, 2025 16:51
@himi
Copy link
Member

himi commented Mar 26, 2025

Ed, thank you for your answer. I found a bit strange behavior of the validation. In KerML, the number of parameter could be checked:

	class C1 {
		feature c11;
		feature c12;
	}
	feature f2 = new C1(1, 3, 4);

This causes an error of `Bound features should have conforming types". However, in SysML, it does not seem to be checked:

	attribute def AD {
		attribute a1 : ScalarValues::String;
		attribute a2;
	}
	
	attribute attr = new AD("str", 5, 6);

Am I missing something? I found the parameter types are correctly checked. So new AD(4, 5, 6) raises an error.

@seidewitz
Copy link
Member Author

Ed, thank you for your answer. I found a bit strange behavior of the validation. In KerML, the number of parameter could be checked:

	class C1 {
		feature c11;
		feature c12;
	}
	feature f2 = new C1(1, 3, 4);

This causes an error of `Bound features should have conforming types". However, in SysML, it does not seem to be checked:

	attribute def AD {
		attribute a1 : ScalarValues::String;
		attribute a2;
	}
	
	attribute attr = new AD("str", 5, 6);

Am I missing something? I found the parameter types are correctly checked. So new AD(4, 5, 6) raises an error.

I believe this is correct per the approved specification, but it is not particularly intuitive, so it is something we might want to address in the RTF.

What is happening here is actually not a check on the number of parameters. Rather, the class C1 and attribute def AD each features inherited from their base library classes, in addition to the owned features given in their declaration. These are ordered after the owned features, so the third argument in each constructor expression is being bound to the first inherited features in each case.

In the case of C1, first inherited parameter happens to be Occurrence::portionOfLife. Since this has type Occurrence, there is a type conflict with the argument 4, which is a data value. On the other hand, in the case of AD, the first inherited feature happens to be DataValue::self. This has type DataValue, so there is no conflict with the argument 6.
(Of course, re-binding self is semantically inconsistent, but that is not being checked statically right now.) If you changed AD to be an item def instead of an attribute def, then you would get a type conflict on the third argument, just like in the case of the class C1 in KerML.

@himi
Copy link
Member

himi commented Mar 26, 2025

Ed, thank you for the detailed explanation. I confirmed if I change attribute def to item def, I got Bound features should have conforming types error. Also if I changed to new AD("str", 5, 6, 7, 8, 9);, I got Must correspond to one feature of instantiated type error.

- Updated TypeAdapter::getFeaturesRedefinedByType to add additional
members before getting redefined features.
@seidewitz seidewitz merged commit a8a7d2f into master Mar 27, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants