Bug 549776 - [language] What do checkonly/enforce really mean?
Summary: [language] What do checkonly/enforce really mean?
Status: NEW
Alias: None
Product: QVTd
Classification: Modeling
Component: Core (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows 10
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Project Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on: 552100
Blocks:
  Show dependency tree
 
Reported: 2019-08-04 08:33 EDT by Ed Willink CLA
Modified: 2019-10-18 10:42 EDT (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ed Willink CLA 2019-08-04 08:33:14 EDT
Making the QVTr Forward2Reverse test generate both directions hits a stupidity.

top relation list2list {
            domain forward ...	   
    enforce domain reverse ...

"reverse" is explicitly enforce so it is enforceable. "forward" is not, so it is implicitly checkable.

When executed towards "forward", both "forward" and "reverse" are treated as inputs giving stupidity.

====

The primary control is that a user specifies which domain is the target; per-domain enforce/check are redundant; the target is in full control.

So the per-domain enforce/check can only be useful for triggering error messages, i.e. if a checkonly domain is enforce that is clearly contrary to the design intent.

====

So as a minimum we have a bug. If all target are enforceable, they are outputs and other checakble/enforceables are inputs. (The inputs should be checkable, but that is probably a pedantry. ow can something be not-checkable?)

If any target is not-enforceable, the invocation is an error.

====

A "checkonly" domain may triger an invocation error / enable a naive synthesize all directions to do less synthesis.

An "enforce" domain is ignored; any not-"checkonly" domain is enfoeable.
Comment 1 Ed Willink CLA 2019-08-04 10:04:35 EDT
(In reply to Ed Willink from comment #0)
> A "checkonly" domain may triger an invocation error 

No. 7.2.3 says that execution of a checkonly in an enforce direction just does a check and may cause a run-time fail accordingly.
Comment 2 Ed Willink CLA 2019-08-17 10:24:49 EDT
(In reply to Ed Willink from comment #1)
> No. 7.2.3 says that execution of a checkonly in an enforce direction just
> does a check and may cause a run-time fail accordingly.

For Benchmarx we may have a more interesting use case.

domain family ...
domain person ...
domain family2personPlan

so that for family2person we first do

check {family, person> => enforce family2personPlan

then

check {family, family2personPlan> => enforce person

which in QVTc might have direction person using direction family2personPlan. QVTr needs this missing declaration.

The sequenced sub-transformations ensure that in the first sub-transformation the old contents of the person model are used to influence the family2personPlan, before the second sub-transformation does a normal enforce/update of gthe person contents.

?? is this sound or just a cute avoidance of a 'clearer' use of the person@pre domain ??
Comment 3 Ed Willink CLA 2019-10-11 15:18:06 EDT
As soon as the Benchmarx implemntation elaborates to cascade out-to-in we get in a mess because the inrermediates are treated as inputs. Cascaded domains are not yet supported.

(In reply to Ed Willink from comment #2)
> ?? is this sound or just a cute avoidance of a 'clearer' use of the
> person@pre domain ??

The '@pre' can be deduced.

Potentially every domain has an @pre and an @post but selection of a direction picks a specific @post and can render many of the @pre / @post redundant.

"checkonly" means that the domain cannot be an output, i.e. it only uses @pre.
"enforce" means that the domain cannot be an input, i.e. it only uses @post.
"" means that the domain can be an input or an output, i.e. it may use @pre or @post, but not both.

Each relation contributes to a directed cyclic graph.

The nodes of the graph are the selected @post domain(s), the corresponding @pre domain(s) and the other domains for which @pre and and @post are not distinguished.

The hyperedges are the relations, with a distinct hyperedge for each possible permutation of @pre/@post connectivity.

It should be possible to traverse the graph backwards from the selected consuming @post domain to identify which producing relations are needed to satisfy consumed domains. ?? Else a design error ??

Yes, but for a bidirectional transformation the cycles are challenging.

Some cycles can be broken by splitting the specified output(s) as @pre/@post.

Suppressing earlier generation of other domains in favur of later ones can help.

But intermediates such as the FamilyPlan still get consumed, in case there is an external input. Can we identify internal intermediates from the existence of metamodel references that go out to elements that are not in used packages? If so we can start to trim the realtions. But is this a secret that will confuse users? Should there be an explicit is-internal declaration?
Comment 4 Ed Willink CLA 2019-10-12 03:24:33 EDT
(In reply to Ed Willink from comment #3)
> But is this a secret that will confuse users?

For trivial 1-in/out 1-out/in TX, specifying the out obviously allows everything to be deduced, but in a more general config that might need to execute as 1-in 1-in+out 1-out 2-intermediate, we can use smarts to provide defaults, but the user must have overall control. Once we provide control the subtleties of whether 1-in+out is a merge/replace can be controlled too.

An "in" TypedModel has a single in/@pre TypedModelInstance.
An "out" TypedModel has q single out/@post TypedModelInstance.
An "in+out" TypedModel has distinct in/@pre and out/@post TypedModelInstances.
An "intermediate" TypedModel has a single internal TypedModelInstance.

An in TypedModelInstance may have zero or more source models.
An out TypedModelInstance may have zero or one target models.
An internal TypedModelInstance may zero or one debug target models.

The trace TypedModel is always "in+out"-replace.
The this TypedModel is always "internal" with no debiug output option.
The primitive TypedModel is always "internal" with no debug output option.

If an "in" TypedModelInstance uses an "out" TypedModelInstance, the TX iterates until there is nothing to do.
Comment 5 Ed Willink CLA 2019-10-13 03:25:16 EDT
Hitherto we have only handled trivial unidirectional in->out or bidirectional left<->right so that designation of the output makes the behaviour obvious; checkonly/enforce are close to gratuitous annotations that are only there to be wrong. Test files did have stupid mistakes.

Once we have domain cascades with distinct forward/reverse relations checkonly/enforce have important subtle significance; the user may need help to get it right.

For now elaboration of Person2FamilyPlanning to Person2FamilyPlanning2Family only needs cascades to be resolved which should be 'easy'; just needs QVTr2QVTs to determine input/output locally rather than globally.
Comment 6 Ed Willink CLA 2019-10-13 08:56:37 EDT
(In reply to Ed Willink from comment #0)
> Making the QVTr Forward2Reverse test generate both directions hits a
> stupidity.
> 
> top relation list2list {
>             domain forward ...	   
>     enforce domain reverse ...
> 
> "reverse" is explicitly enforce so it is enforceable. "forward" is not, so
> it is implicitly checkable.
> 
> When executed towards "forward", both "forward" and "reverse" are treated as
> inputs giving stupidity.

This is the previaling helpful functionality.

===========================

If we examine QVT 1.3.

7.11.1.3 "A domain may be marked as checkable or enforceable." narrowly only allows checkable or enforceable and the subsequent description gives no clue as to what to do if (not-checkable and not-enforceable) or (checkable and enforceable) are used.

7.13.7 modelDomainCS has three possibilities. Only the checkonly case of which corresponds to the sensible combinations above.

Looking at concrete examples:

The unidirectional RelToCore is blank-to-enforce.

The unidirectional UML2RDBMS is almost universally checkonly-to-enforce.

There is no bidirectional example.

(In reply to Ed Willink from comment #1)
> (In reply to Ed Willink from comment #0)
> > A "checkonly" domain may triger an invocation error 
> 
> No. 7.2.3 says that execution of a checkonly in an enforce direction just
> does a check and may cause a run-time fail accordingly.

This is weird. Consider UML2RDBMS with checkonly Schema/Table but enforce Columns. Does this mean that an enforce that only changes columns changes the model whereas one that needs to change Tables fails? Ridiculous. The immutable target imposition is much more compactly specified once on the TypedModel rather than repeatedly and perhaps inconsistently on each domain.

For a complex unidirectional transformation such as RelToCore, reverse execution is a near impossibility, certainly only possible after a substantial amount of unnecessary design work. But with checkonly 'inputs' a reverse execution is required to see whether the QVTc transforms to a consistent QVTr, which is 99.99% of the support required to transform QVTc to a saveable QVTr. If a real check is required then the user can check that the QVTr transforms to a consistent QVTc; no fantastical reverse capability necessary.

For simple A-to-B transformations we just a need a single declaration as to whether reverse execution is permissible.

Optionally we may have a further declaration that prohibits modification of a domain used as the target to prevent an attempt to launch an undesirable mutation.

Both the above provide competing semantics for a TypedModel-level "checkonly" (or not-"enforce"). A plausible resolution of the competition is:
"" => execution in the direction is not supported
"checkonly" => execution in the direction checks the transformed output
"enforce" => execution in the direction may create/replace the output
But this is not backward compatible; perhaps all TypedModels as "" implies all as "enforce".

Once we consider relation cascades, it should be possible to have separate relations for each direction. The same prohibited/checkonly/enforce options apply.

Overall we three levels of control
- user specifies the target TypedModel(s) and whether to check/enforce/...
- transformation specifies whether each TypedModel as output is prohibit/checkonly/enforce
- relation specifies whether each domain as output is ignore/checkonly/enforce

For compatibility we want blank and enforce to be the same default. "checkonly" is useable. Need a new keyword for prohibit/ignore; perhaps "infeasible"/"undefined".
Comment 7 Ed Willink CLA 2019-10-14 03:07:38 EDT
Compatible way forward. If all current checkonly/enforce is gratutious we can just lose it and only support new functionality.

Surprisingly all QVTr/QVTc tests (except the serializer tests) pass after making checkonly/enforce ignored in the editor. Trimming 'checkonly' | 'enforce' from AS2CS, forcing isCheckable=true, isEnforceable=true in the AS and the serializer tests pass too; fortunately we only compare *.qvt?as. 

=========================================================================

For each domain / typed-model there are three modes.

"sourceonly" - execution cannot be towards the domain. No create/update/compare/check.

in text: blank.
in AS: isCheckable = false, isEnforceable = false.
in TypedModel AS: isSourceonly = true, isReadonly = false.

"checkonly" - execution can be towards the domain. Target is compared/checked.

in text: blank.
in AS: isCheckable = true, isEnforceable = false.

"enforce" - execution can be towards the domain. Target id created/updated.

in text: blank.
in AS: isCheckable = true, isEnforceable = true.

This is actually exactly what the spec says, the problem is an attempt to misread "checkonly" as read/input.

A blank to blank TX is illegal - nothing can be a target.
A blank to enforce is unidirectional.
An enforce to enforce is bidirectional.

A checkonly to enforce is semi-bidirectional forward works, reverse can only be checked.
A checkonly to enforce can only check bidirectionally.
A blank to checkonly can only check (but not enforce) forwards

The semi bidirectional case is pretty stupid and is unfortunately what is used throughput the main text examples encouraging the misreading.

All checkonly cases seem pretty stupid. Surely the TX should be written as enforce but the execution invocation may choose to only check existing models?

With !checkable, !enforceable as an illegal target, we can happily issue warnings if the Domain check/enforce is wider than the TypedModel check/enforce. We can also warn about Relations with no check/enforce. We can AND the domain check/enforce with the invocation to identify dead relations, thereby selrct the appropriate {enforce-A blank-B} / {blank-A enforce-B).
Comment 8 Ed Willink CLA 2019-10-14 03:41:07 EDT
https://issues.omg.org/browse/QVT14-66 raised.
Comment 9 Ed Willink CLA 2019-10-15 04:06:49 EDT
(In reply to Ed Willink from comment #3)
> Each relation contributes to a directed cyclic graph.

One day it might be nice to build and solve this.

But for now it is sufficient to impose a requirement that the invoker specify a correct ordering of intermediate TypedModels.

One day moved to Bug 552098.
Comment 10 Ed Willink CLA 2019-10-15 04:15:44 EDT
(In reply to Ed Willink from comment #6)
> Optionally we may have a further declaration that prohibits modification of
> a domain used as the target to prevent an attempt to launch an undesirable
> mutation.

Revamping the launch configuration moved to Bug 552100.
Comment 11 Ed Willink CLA 2019-10-17 03:37:45 EDT
(In reply to Ed Willink from comment #7)
> This is actually exactly what the spec says, the problem is an attempt to
> misread "checkonly" as read/input.

Bug 552162 corrects the confused usage of "check" to mean "input" in QVTi.