Open eclipse-qvtd-bot opened 1 day ago
By Ed Willink on Apr 17, 2017 06:48
This prototypes part of the solution to http://issues.omg.org/browse/QVT14-6 .
By Ed Willink on Apr 18, 2017 11:54
(In reply to Ed Willink from comment #0)
(Optimization: a single invocation might allow TA to inherit SB.)
Use of SB makes no difference to the B mapping, apart from eliminating the need for multiple per-caller variants. The one mapping predicates SB rather than TA.(If both when and where invocations are used, we need two implementations, one predicating and one realizing the enforced root variable(s).)
If TA inherits SA, we have no change to A, but we need to ensure that the SA property names are unique across all S, T to avoid collisions from multiple distinct invocations. Unfortunately this fails for the unusual case of multiple invocations of the same mapping, for which TA must aggregate rather than inherit multiple SB.
Since (multiple) aggregation of SB into TA always works, but costs on trace object size, leave inheritance as a future optimization. The aggregation is probably unnecessary except that we want regular modeled objects and relationships. The aggregation can probably be a volatile non-containment to help the garbage collector reduce costs.
By Ed Willink on Apr 19, 2017 07:13
(In reply to Ed Willink from comment #0)
Synthesizing the refinement dispatcher is a QVTs2QVTi / QVTi runtime problem.
The S* instances should be queued to relevant Connections by the current scheduler. Each Connection therefore just needs to be aware of the polymorphism of its consumers in order to dispatch appropriately. Incremental re-execution should be straightforward.
By Ed Willink on Apr 22, 2017 18:07
(In reply to Ed Willink from comment #0)
Synthesizing the SB calls is a QVTr2QVTc problem.
Yes, but QVTr supports single Rule::overrides, while QVTc supports multiple Mapping::specification. overrides is a replacement semantics, specification is a refinement syntax, QVTc has no CS for Rule::overrides.
a) is the omission of QVTc overrides syntax an oversight? easily remedied.
b) how could QVTc do overrides as is?
every mapping must have predicates to exclude their overriding matches. Simple but inefficient repetitious dispatch unless the idiom is recognized and optimized.
perhaps the family of predicates can be realized by an operation or family of virtual operations.
guard Dispatch'family-name'(invocation arguments) == 'my-family-id'
guard idiom: mappings sharing the same source connection with similar guards can migrate the guard to the connection.
An operation seems relatively easy to recognize and exploit. No need for QVTC change.
By Ed Willink on Apr 23, 2017 00:14
(In reply to Ed Willink from comment #4)
guard idiom: mappings sharing the same source connection with similar guards can migrate the guard to the connection.
See Bug 508267, This is precisely what the 'alternator' was doing. In the context of Connections it is more regular. We can tackle it in two parts.
This bug: synthesize a function to select the overload from the arguments, and ue the function in each predicate.
Bug 508267: hoist the common predicate structure from invoked micromappings into the invoking connection.
Families2Persons::isFemale already gives us a test.
By Ed Willink on Apr 23, 2017 00:58
(In reply to Ed Willink from comment #5)
synthesize a function to select the overload from the arguments,
Take a 'QVT' example, with Class->ClassDash, Package->PackageDash rules. What happens to a Transformation (which is Class+Package)? Both rules must execute but cannot sensibly synthesize TransformationDash by a run-time merge unless we have a new capability for synthesized run-time types. The missing Class+Package->... rule must be a run-time error.
By Ed Willink on Apr 23, 2017 03:44
(In reply to Ed Willink from comment #6)
The missing Class+Package->... rule must be a run-time error.
NO. This is being maximally unhelpful. There is a class of transformations for which executing each of Class->... and Package->... independently for a Class+Package is useful. An 'EMOF' statistics analyzer should not have to be rewritten just because QVT has a suspect inheritance.
Perhaps a run-time 'strictness' option can enable/disable multi-matching. See 515619.
By Ed Willink on Apr 23, 2017 04:04
Potentially any dispatch might encounter an horrendous run-time multi-match if the user provides an A+B+C+D+.... instance to transform. This makes it difficult to statically prepare a simple dispatch table; all mappings must be considered.
For the very common case of single type dependence, we can use an EClass-to-array-of-mappings lookup dispatcher, eagerly populated by the conformant EClasses known at compile-time. Lazy entries can be added on demand for the necessary but rare case that new EClasses appear at run-time.
Multiple type dependence could use cascaded lookups or a hashed argument lookup. Many implementation options, potentially influenced by the number of overrides and the number of EClasses.
But what about non-type guards? If MB for a B=>... overrides MA for an A=>... and MB has a guard G, what happens for a B for which G=false?
If the programmer requires something to always happens and so provides MA as a fallback, then MA should execute.
However if the programmer requires that non-execution is a permitted behaviour, then MA should not execute.
Is this a free design choice?
Emulating maybe-execution with always-execution is hard.
Emulating always-execution with maybe-execution is tedious; an additional MB2 for not G can be added with a when delegation to MA.
Abstractly, a mapping has a variety of guard semantics; type conformance is just one and so the internal guards and the external signature should not discriminate.
Practically, if the internal predicates are complex and involve speculation it may be very hard to fallback to try something else.
By Ed Willink on Apr 23, 2017 04:24
(In reply to Ed Willink from comment #8)
Emulating maybe-execution with always-execution is hard.
Unless the base mapping has its own non-execution capability which can be re-used by the overriding mappings.
By Ed Willink on May 16, 2017 10:10
Enough rambling. Time for some design.
By Ed Willink on May 17, 2017 04:37
(In reply to Ed Willink from comment #10)
Enough rambling. Time for some design.
Where there is a solution.
For QVTr there is a rewrite so that evety relation synthesizes a guard function, which may predicate on the guard functions of overriding relations.
Efficient QVTi dispatch is Bug 508267.
By Ed Willink on May 18, 2017 13:53
http://issues.omg.org/browse/QVT14-52 identifies that the overrides/overridden are perverse. Clean it up while we're in the area.
By Ed Willink on Aug 09, 2017 10:59
Implementing overrides within QVTr2QVTc is too hard, so use Bug 520768's AbstractQVTr2QVTr.
By Ed Willink on Aug 18, 2017 02:28
Using the new AbstractQVTr2QVTr capability a QVTr-with-overrides to QVTr-without-overrides is really easy. We strip the 'overrides' declaration and and a not predicate on each transitively overriding relation.
(In reply to Ed Willink from comment #6)
Take a 'QVT' example, with Class->ClassDash, Package->PackageDash rules. What happens to a Transformation (which is Class+Package)? Both rules must execute but cannot sensibly synthesize TransformationDash by a run-time merge unless we have a new capability for synthesized run-time types. The missing Class+Package->... rule must be a run-time error.
The simple overrides rewrite means that a Transformation will transform to both a ClassDash and a PackageDash; not wrong. If the user provides a Transformation->TransformationDash override then it is sensible.
Perhaps a diagnostic could be provided for when independent supertypes are independently transformed. Separate issue; see Bug 521095.
By Ed Willink on Aug 20, 2017 09:57
(In reply to Ed Willink from comment #14)
We strip the 'overrides' declaration and and a not predicate on each transitively overriding relation.
Not quite so simple. The anded-not-predicates ensures that overridden relations do not execute, however we also need ored-predicates at each 'when' invocation to tolerate whichever actually executes.
Using the new AbstractQVTr2QVTr capability a QVTr-with-overrides to QVTr-without-overrides is really easy.
Unfortunately the overrides declaration is needed to ensure that the no-non-top relations transformation generates correct invocation class/interface hierarchy.
Now the overrides declaration is still needed for correct trace class/interface hierarchy.
Injecting the invocations package/domain into the QVTr-without-non-tops is a bit klunky.
Now that we understand that non-top reification requires a hierarchy of invocation classes and that when results require a hierarchy of trace classes without any additional relations perhaps invication and trace class/interface synthesis should be done together. Given sensible interfaces perhaps the and/or can be evaluated over the collection of conformant interfaces without explicitly enumerating them.
Trace class design in https://wiki.eclipse.org/QVTd_Traces_and_Invocations
By Ed Willink on Aug 23, 2017 03:28
(In reply to Ed Willink from comment #15)
Now the overrides declaration is still needed for correct trace class/interface hierarchy.
Introducing a dispatcher relation/mapping avoids the interdependencies.
Every invocation of an overriding/overidden relation is redirected to an invocation of a dispatcher relation comprising a when and a complex if dispatch tree of all possible direct calls. This avoids the need for non-trivial trace/invocation class hierarchies; all calls are direct to the unambiguous relation.
Naive dispatcher for A1 overrides B, A2 overrides B, B overrides C
when {\ A1(...) or\ A2(...) or\ ((not A1(...) and not A2(...)) implies B(...)) or\ ((not A1(...) and not A2(...) and not B(...)) implies C(...));\ }
The dispatcher is non-top if any of the overriding/overidden relations is non-top.
By Ed Willink on Aug 25, 2017 03:27
(In reply to Ed Willink from comment #16)
Introducing a dispatcher relation/mapping avoids the interdependencies.
Rubbish. In the absence of an if-capability from Bug 480194, it is impossible to implement a single mapping override dispatcher in QVTm or QVTi. A dispatcher requires an override-specific invocation/trace class to be created, but there is no ability to conditionalize individual realize/new statements.
For non-top overrides there is no alternative to a (covariant) polymorphic invocation class hierarchy.
For top non-overrides or top overrides there is no invocation other than object matching so introducing an invocation class could distort matching/execution.
For top overrides there is an option to merge the one invocation class with the one known trace class and pass the trace class to the invoked mapping.
For non-top overrides and non-top overrides where there are not multiple invocations for the same mapping there is an option to merge the invocation class with invoking trace class rather than containing it.
By Ed Willink on Aug 25, 2017 03:43
(In reply to Ed Willink from comment #17)
For non-top overrides there is no alternative to a (covariant) polymorphic invocation class hierarchy.
and for non-top when overrides there no alternative to (at least) two-parts. A first part realizes the invocation class. A second part exploits the invocation result.
By Ed Willink on Aug 25, 2017 09:39
(In reply to Ed Willink from comment #18)
and for non-top when overrides there no alternative to (at least) two-parts.
Therefore use Bug 521113 for the more troublesome non-top relations case.
This bug is restricted to the simpler top override relations.
By Ed Willink on Sep 11, 2017 16:12
(In reply to Ed Willink from comment #19)
This bug is restricted to the simpler top override relations.
and where non-top
Pushed to master for M2.
By Ed Willink on Dec 21, 2017 11:38
Still not so simple.
If C extends B extends A\ and C2C overrides B2B overrides A2A
execution of B2B depends on a guard match of B2B and a guard mis-match of C2C; the guard match/mis-match is used twice, if successful by its execution if unsuccessful by overridden executions. The guard mismatch is not available as part of an execution.
We must have mapping pairs; gB2B that evaluates the guard/predicate match and never fails. eB2B the executes and succeeds/fails according to gB2B match/mismatch.
Once we have pairs, non-top is easy. We just have an extra input for the non-top invocation request instance. An alternate QVTr2QVTc synthesis into gX + eX rather than X seems an unavoidable pain.
By Ed Willink on Dec 22, 2017 04:47
(In reply to Ed Willink from comment #21)
We must have mapping pairs; gB2B that evaluates the guard/predicate match and never fails. eB2B the executes and succeeds/fails according to gB2B match/mismatch.
See Bug 529130 for the major restructuring to abandon QVTr2QVTc in favour of QVTr2QVTg so that we have a credible model in which to perform the splitting into mapping pairs.
| --- | --- | | Bugzilla Link | 515327 | | Status | REOPENED | | Importance | P3 normal | | Reported | Apr 16, 2017 11:02 EDT | | Modified | Dec 22, 2017 04:47 EDT | | Depends on | 529130, 520768 | | Blocks | 522340, 462165, 514590, 515236, 521113 | | See also | 508267, 515619, 480194 | | Reporter | Ed Willink |
Description
Currently when relation A when's/where's B an invocation-specific B A_when_B, A_when_B1 etc is created. No consideration has been given to overrides. Obviously we need A_when_Bdash for each overriding Bdash and an appropriate dispatcher. This scales really badly. Consider ATL2QVTr where perhaps 10 expression classes are copied in perhaps 10 places. Oops we need 10*10 invocation-specific expression mappings and repeated invocation site dispatchers.
Why? B must wait for A, so the TA_when_B has dependency relationships to TA. But B has no need to access all of TA; just the bindings to B's root variables. Therefore we can have a single implementation of B maintaining the TB trace, and also using the SB signature. A therefore populates an SB that is queued for dispatch to a B or Bdash; no need for per-A overheads. To accommodate multiple invocations, each SB can be aggregated in the caller (TA).
(Optimization: a single invocation might allow TA to inherit SB.)
We do not need every possible SB, just an SB for each statically invoked B. The inheritance hierarchy of the SBs mirrors the overrides hierarchy of the Bs. The dispatcher for each SB takes responsibility for sequencing the guard checks of statically consistent overriding Bs and converting the calling SB to the SBdash where this differs.
Synthesizing the SBs is a QVTr2Trace problem.\ Synthesizing the SB calls is a QVTr2QVTc problem.\ Synthesizing the refinement dispatcher is a QVTs2QVTi / QVTi runtime problem.
This mechanism should be exploitable by a QVTr2QVTc auto-copy generator.
This mechanism should be extensible to multiple overrides (not yet allowed by QVTb).