Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "QVTd Relation Overriding"

(QVTr2QVTc Overrides 'Rewrite')
Line 67: Line 67:
  
 
The CSE will need to work on the OCL expressions and so we need to generalize CSE support from CG-only. [https://bugs.eclipse.org/bugs/show_bug.cgi?id=516751]
 
The CSE will need to work on the OCL expressions and so we need to generalize CSE support from CG-only. [https://bugs.eclipse.org/bugs/show_bug.cgi?id=516751]
 
==The low level / run-time mechanism==
 
 
(Temporarily ignoring the additional complexities of multi-headed mappings.) When an element is dequeued from a Connection, it is dispatched to all relevant mappings. This remains true for overrides. Predicates and type guards will suppress most irrelevant invocations, but we must suppress overrides. There will often be only one appropriate mapping, but we cannot avoid the possibility that the input model has new compound types that are multiply matched. We cannot avoid a multiplicity of matches and a lattice of fall-backs for failed predicates.
 
 
The original motivation for an alternator was to avoid repetition. If the alternator hoists all predicate functionality from all dispatch candidates, we maximize sharing, and can resolve all the fall-backs before 'returning' with the list of actual dispatches.
 
 
Perhaps the alternator is a function that is invoked as the predicate of each override. Possible because the failed predicates should participate in a decision tree. But how to pass context.
 
 
If the alternator is a mapping, it has a trace class that can have all the required shared content. The overrides just predicate the alternator-trace-class and its magic decision variables. Simple override choices may use a simple alternator class to avoid bloating the alternator trace with all the context for every awkward choice. The alternator therefore conditionalizes its realized trace variable. But QVTc only conditionalizes by using one mapping per type which destroys the whole purpose. The QVTc alternator must create a ragbag of all possibilities. Optimizing this to exclude default valued-elements will have to be a QVTs/QVTi optimization.
 
 
== The QVTc mechanism==
 
 
For each distinct polymorphic dispatch there is a single alternator mapping that hoists the predicates of all possible targets and encodes the dispatch decision, perhaps as a few Booleans, perhaps as an Enumeration, ... . Each dispatch target uses the alternator trace as its predicate with appropriate matches on the decision.
 
 
What about an override that participated in multiple distinct polymorphic dispatches?
 
 
Simple case, one is a super-type of the other. Re-use the larger ragbag, but provide distinct alternators to avoid run-time computation bloat.
 
 
Horrible case, multiple inheritance. Simple solution, ragbag the lot. More optimum, multiple trace class inheritance.
 
 
==The QVTr2QVTc synthesis==
 
 
The alternator trace ragbags all the mapping predicates. For each mapping predicate we get a Boolean go/no-go, which may need to be reified in the alternator trace for run-time dispatch. Where we can prive that some of these Booleans are orthogonal we can compress to an Enumeration of orthogonal alternatives.
 
 
The synthesis of the alternator mapping must treat predicates differently since only a total failure may fail. Individual predicate failures must set the state of corresponding trace class state.
 
 
* identify all polymorphic 'calls' - top/when/where
 
* identify distinct polymorphisms - an alternator for each
 
* build 'inheritance' tree/lattice - trace class per leaf
 
* gather trace class properties from each polymorphic element
 
* synthesize alternator for predicates of each polymorphic element
 
* apply CSE
 
* synthesize each polymorphic element using its alternator trace class as predicate
 
 
===Aggregated predicates===
 
 
Aggregating the predicates highlights a problem. What if a predicate involving a when succeeds, but its result is unsatisfactory. For the particular case of a type check, the required type could be pushed into the invocation. But for a more general case where perhaps two when's are invoked and their results checked for inequality, how is each 'successful' when invocation ignored? Not even speculation will cope with code that rejects the full results. Prohibiting rejectable when's seems too strong a limitation.
 
 
QVTc does not actively invoke anything, rather dependencies just wait for good things to have happened. It should not therefore be necessary to roll-back anything, certainly not for the 'look-up when'. Rather we need to ensure that the 'global' dependencies are soluble. Challenging scenario added to [https://bugs.eclipse.org/bugs/show_bug.cgi?id=516774].
 
 
Another problem. The alternator may need to be multi-stage in order to avoid unnecessary invoking when predicates that are occluded by successful overrides.
 

Revision as of 04:29, 17 May 2017

The QVT specification has a relatively clear specification that QVTr relation may override, but no clues as to how this is mapped to QVTc that has no overrides concrete syntax, even though it inherits an overrides capability from QVTb.

This working document considers how overriding might be supported. It takes over from

QVTc Choices

QVTc refinement syntax is an additive capability and therefore not suitable for implementing the QVTr replacement semantics of QVTr overrides.

We could add overrides syntax and semantics to QVTc, which then passes the buck to QVTi. But since we already need an 'alternator' to dispatch multiple similar but differently predicated mappings efficiently, we may be able to kill two birds with one stone.

QVTc is relatively simple. It solves the mapping sequencing problem by inter-trace class references/dependencies. Can it solve the overrides problem as-is? Let's go with this.

QVTr2QVTc Overrides 'Rewrite'

There is a simple rewrite that reifies overrides in QVTc without requiring any new capabilities, although it may stress some existing ones.

Given a transformation comprising Mx mappings/relations, Gx guard functions, and Bx bottom actions:

MA: if GA() BA();
MB overrides MA: if GB() BB();
MC overrides MB: if GC() BC();
MD overrides MA: if GD() BD();

we can rewrite as:

MA: if GAdash() BA();
MB: if GBdash() BB();
MC: if GCdash() BC();
MD: if GDdash() BD();
GAdash() = !GBdash() && !GDdash() && GA();
GBdash() = !GCdash() && GB();
GCdash() = GC();
GDdash() = GD();

i.e. introduce and use Gxdash() equal to Gx() anded with the Gxdash()es of all directly overriding relations.

Rewrite reification in QVTr2QVTc

We need to synthesize Gx() as a Function/Operation invoked from the Mapping Guard rather than being inlined in the Mapping Guard. All type checks must use explicit oclIsKindOf's since we cannot exploit 'mismatch is predicate fail' semantics within Function/Operations.

Whether Function/Operation should be used to exploit/bypass uniqueness optimizations is unclear. Probably doesn't matter. If QVTi understands what's happening, QVTi can optimize merged invocations behind the uniqueness of the polymorphic dispatcher.

Should relations that are not overridden also have their predicates structured as guard functions?

  • pro: a simplification reducing QVTr2QVTc complexity
  • pro: sibling (similar input, distinct predicate) mappings are also exposed to QVTi dispatch optimization
  • con: increases stress on QVTs deep operation analysis - needs to work anyway
  • con: further deviation from RelToCore

2 strong pro's, 2 weak con's => always create guard functions.

A distinct guard function may allow a failed guard to have a distinct and more optimum trace class for incremental update.

Rewrite reification in QVTc, QVTu, QVTm, QVTs

There should be no change, but deep operation dependency analysis will need to work for QVTs.

Rewrite reification in QVTi / run-time

Nothing necessary, but significant optimizations available.

The dispatch of one or Micromappings from a Connection currently invokes each relevant Micromapping. No change since each synthetic override has full exclusions in its own predicate. However there may be many available Micromappings each with a guard function. These can be hoisted into a dispatcher and CSE applied to dramatically reduce recomputation. Since the failed guard functions are invoked by the dispatcher, the failures do not need to be traced, incremental redispatch from the Connection will redetermine what needs to be done correctly. These optimizations apply to siblings as well as polymorphs.

If we add a QVTi syntax to support the decision tree, the optimized dispatch can work in interpreted as well as code generated execution.

The CSE will need to work on the OCL expressions and so we need to generalize CSE support from CG-only. [1]

Back to the top