ModelDriven / Alf-Reference-Implementation

Open-source implementation of the Action Language for fUML (Alf) specification.
30 stars 2 forks source link

Improper resolution of a classifier template parameter used as a template actual parameter #87

Closed seidewitz closed 5 years ago

seidewitz commented 5 years ago

Consider the following example activity for reversing a sequence:

activity Reverse<T>(seq: T[*] sequence): T[*] sequence {
    return seq->isEmpty()? null:
        seq->excludeAt(1)->Reverse<T>()->including(seq[1]);
}

This parses and executes successfully with our without the explicit template binding for Reverse<T>. However, if the return statement is used as the body of the equivalent template activity in an external UML model, then there is a returnStatementContext constraint violation because of a type incompatibility between the return statement and the return type, but only when the explicit binding Reverse<T> is used. If the recursive Reverse call is implicitly bound, there is no violation.

seidewitz commented 5 years ago

When the template activity is represented in Alf, T is a template parameter, which, in the Alf abstract syntax, is a kind of classifier. However, when the activity is represented in UML, it has a template classifier parameter whose parametered element is the actual classifier T. The parameters of the activity are typed by the parametered element, not the template parameter.

In the case of Reverse being a UML activity, the T in the explicit binding Reverse<T> gets resolved to an external reference to the template parameter, not the parametered element. However, for the purposes of Alf processing, a classifier template parameter is considered to effectively be a classifier, even though the "real" classifier is the parametered element. The template parameter substitution for Reverse<T> then results in the return type for the invocation being the template parameter.

Next, the invocation of the (template) collection function including is implicitly bound to the effective common ancestor of the types of the two arguments to the function. The first argument is the result of the Reverse<T> invocation, which, as indicated above, has the classifier template parameter as its type. The second argument, seq[1], however, has the actual parametered element classifier as its type.

Finally, when the effective common ancestor is computed, the classifier template parameter is not "deferenced" to its parameter element. Therefore, no common ancestor is found between it and the actual classifier that is the type of seq[1], resulting in the effective common ancestor any. And this is not compatible with the return type of the Reverse activity.

If, on the other hand, an implicit invocation is used for the call to Reverse, then the implicit binding is the type of seq->excludeAt(1), which is the actual paramtered element classifier. This is then the same as the type of seq[1], so also becomes the result type for the call to including, which is compatible with the return type of the activity.

seidewitz commented 5 years ago

Fixed in v1.1.0h.