eclipse-qvto / org.eclipse.qvto

Eclipse Public License 2.0
0 stars 1 forks source link

Duplicate result on mapping inheritance #955

Open eclipse-qvt-oml-bot opened 5 days ago

eclipse-qvt-oml-bot commented 5 days ago

| --- | --- | | Bugzilla Link | 489271 | | Status | NEW | | Importance | P3 normal | | Reported | Mar 09, 2016 04:59 EDT | | Modified | Sep 21, 2016 11:14 EDT | | Reporter | Christopher Gerking |

Description

According to the QVT 1.3 Beta, the init section of an inherited mapping is executed after the implicit instantiation section of the inheriting mapping (including the creation of the trace record). This is problematic because the existing result from the inheriting mapping (which is also on the trace) may be reassigned in the init section of the inherited mapping. This duplicate result leads to inconsistent trace records between the two mappings, which should share the same traced result.

The bigger problem is that the type of the reassigned result is not necessarily compatible with the inheriting result type:

mapping m1() : EClass inherits m2;

mapping m2() : EClassifier {\ init {\ result := object EDataType {};\ }\ }

This will lead to some kind of a runtime error, because the EDataType can't be passed on to the inheriting mapping of type EClass.

I see two options to handle this problem:

1.) An inherited mapping must not have an init section (and otherwise causes a syntax error on the inheriting mapping).

2.) The execution of the inherited init section is always suppressed at runtime.

eclipse-qvt-oml-bot commented 5 days ago

By Sergey Boyko on Mar 09, 2016 08:55

Hi,

I think that specification is not correct at this part.

There are two places in the document where the behavior is declared:

a) Section "8.1.15 Reuse Facilities for Mapping Operations"\ b) Section "8.2.1.15 MappingOperation"

The specification states that inheriting mappings invokes first its initialization section, including the implicit instantiation section, and then invokes the inherited mappings including their's initialization section. \ In case when initialization section of inherited mapping contains assigment to result variable such behavior will result in that the mapping returns object differ than one stored in trace-record. This breaks fundamental property of mapping definition.

The problem is that the order of calls of initialization sections has been inverted. To be correct the order of calls should be the same as for the mappings population sections. \ In other words initialization section of inherited mappings should be invoked first, then initialization section of inheriting mapping and then the implicit instantiation section.

Given the following mappings are defined as follows (pseudocode):\ maping a() {}\ maping b() {}\ maping c() inherits a {}\ maping d() inherits b, c {}

Then in case of mapping invocation\ map d()

The correct order of calls should be\ b()-when{}\ a()-when{}\ c()-when{}\ d()-when{}

b()-init{}\ a()-init{}\ c()-init{}\ d()-init{}

d()-implicitInstantiation_and_addToTrace\ b()-implicitInstantiation_and_addToTrace\ c()-implicitInstantiation_and_addToTrace\ a()-implicitInstantiation_and_addToTrace

b()-population{}\ a()-population{}\ c()-population{}\ d()-population{}

b()-where{}\ a()-where{}\ c()-where{}\ d()-where{}

eclipse-qvt-oml-bot commented 5 days ago

By Ed Willink on Mar 09, 2016 09:21

(In reply to Sergey Boyko from comment #1)

The correct order of calls should be b()-when{} a()-when{} c()-when{} d()-when{}

...

Seems sensible except that

d()-implicitInstantiation_and_addToTrace b()-implicitInstantiation_and_addToTrace c()-implicitInstantiation_and_addToTrace a()-implicitInstantiation_and_addToTrace

the order here is irrelevant. There is only ONE trace record, with FOUR trace identities.

QVT 1.3 8.1.11.1 makes an attempt at defining the hidden information content in a trace record. In particular:

• invoked-mapping - the invoked mapping operation\ • executed-mapping - the executed mapping operation

Eclipse QVTo needs to move a bit to accommodate these alternatives. Perhaps executed-mapping should be multiples, so that for your example:

invoked-mapping = X1::d\ executed-mapping = { X2::d, X3::B, X4:c, X5::a }

eclipse-qvt-oml-bot commented 5 days ago

By Ed Willink on Mar 09, 2016 09:27

OMG Issue http://issues.omg.org/browse/QVT14-26 links to this Bug and now vice-versa.

eclipse-qvt-oml-bot commented 5 days ago

By Ed Willink on Mar 09, 2016 10:24

Some of my earlier comment got lost.

We should define how an 'equivalent' flattened mapping is formed from the inherited mappings, then only the 'equivalent' flattened mapping is actually executed. A QVTo to Java CG might actually reify and execute this 'equivalent'.

Sergey's example suggests a section-wise concatenate for when/where/init/population in bottom-up, left-to-right order. The 8.1.11.2 pseudo-code seems ok for the trace so long as we change executed-mapping to executed-mappings also in bottom-up, left-to-right order.

eclipse-qvt-oml-bot commented 5 days ago

By Christopher Gerking on Mar 09, 2016 10:37

(In reply to Sergey Boyko from comment #1)

I think that specification is not correct at this part.

I agree.

In case when initialization section of inherited mapping contains assigment to result variable such behavior will result in that the mapping returns object differ than one stored in trace-record. This breaks fundamental property of mapping definition.

That's exactly one of the problems I explained above.

In other words initialization section of inherited mappings should be invoked first, then initialization section of inheriting mapping.

That solves the problem you just mentioned and overlaps with what is written here: http://www.levysiqueira.com.br/2012/06/inheritance-in-qvto/

However it doesn't solve the basic problem of executing multiple incompatible init sections. See below:

The correct order of calls should be b()-init{} a()-init{} c()-init{} d()-init{}

There is no guarantee that the result produced by b() is type compatible with the result required by a() or c() or d(). Thus, if the result from b() is incompatible and d() doesn't override it, there would be a runtime error.

The spirit and purpose of an init section is to create an explicit result. Everything else can be moved to the later sections. Since only one result must be created per mapping invocation, I see no need to execute more than one init section. The only mapping that can create an evidentially compatible element is the most inheriting one, so d() in the above example. Therefore, the only init section that executes should be the one from the invoked (most inheriting) mapping.

eclipse-qvt-oml-bot commented 5 days ago

By Ed Willink on Mar 09, 2016 11:06

(In reply to Christopher Gerking from comment #5)

The spirit and purpose of an init section is to create an explicit result.

Not entirely. The pseidocode from 8.1.11.2 is:

init { … }\ // instantiation section\ if (p3 == null) p3 := object P3{};\ if (r1 == null) r1 := object R1{};\ if (r2 == null) r2 := object R2{};

The init code inhibits the following default object creations. So concatenating init sections is fine, but may be redundant. Sergey',s bottom-up, left-to-right order is good, the most derived can override predecessors.

If multiple init's assign to the same variable the last one takes precedence.

We have a design choice

-trivial - execute overridden assignments regardless\ -declarative - discard only what is provably dead and side-effect free\ -imperative - discard all assignments that are overridden.

Imperative is efficient and easy; there could be a warning if a discarded init assigmnent is used by another not-discarded init assignment.

eclipse-qvt-oml-bot commented 5 days ago

By Christopher Gerking on Mar 09, 2016 11:16

(In reply to Ed Willink from comment #6)

If multiple init's assign to the same variable the last one takes precedence.

Yes but what if the last init doesn't reassign, and the prevailing value is incompatible? That's possible despite all the current syntactic restrictions:

maping a() : EClassifier { result := object EDataType{} } maping c() : EClass inherits a { -- no reassignment of result }

Although EClass is compatible with EClassifier, the actual result from a() may be an incompatible EClassifier, for example an EDataType that is not an EClass.

eclipse-qvt-oml-bot commented 5 days ago

By Ed Willink on Mar 09, 2016 11:25

(In reply to Christopher Gerking from comment #7)

Although EClass is compatible with EClassifier, the actual result from a() may be an incompatible EClassifier, for example an EDataType that is not an EClass.

Compile-time error. The equivalent flattened mapping must be satisfactorily identifiable. User may have to put in the re-assignment to make the WFR Constraint violation go away.

eclipse-qvt-oml-bot commented 5 days ago

By Christopher Gerking on Mar 09, 2016 11:50

(In reply to Ed Willink from comment #8)

Compile-time error.

Where? All the mappings are locally valid. The error arises only from the mapping reuse. How to detect that error at compile time? The invalid 'result' assignment may be nested arbitrarily deep inside the init block, and may be hidden inside further operation calls:

maping a() : EClassifier { result := getSomeClassifierThatIsReallyADataType() }\ maping c() : EClass inherits a {}

Seems like a major validation overhead.

eclipse-qvt-oml-bot commented 5 days ago

By Ed Willink on Mar 09, 2016 12:32

(In reply to Christopher Gerking from comment #9)

maping a() : EClassifier { result := getSomeClassifierThatIsReallyADataType() } maping c() : EClass inherits a {}

Seems like a major validation overhead.

Just one of many OCL WFRs, which should eventually be in the QVT spec.

In this case it's not that hard

gather all flattened mappings\ gather all inits\ for each distinct init\ check that each init initializer is compatible with its flattened init

(The Pivot-based OCL uses OMG candidate WFRs in Pivot.ocl and generates them as Java for use by the real AS validation. In due course we should do this for QVT too.

eclipse-qvt-oml-bot commented 5 days ago

By Ed Willink on Mar 09, 2016 12:45

(In reply to Ed Willink from comment #10)

getSomeClassifierThatIsReallyADataType() }

Forgot to point out that while getSomeClassifierThatIsReallyADataType() may be horrendously complicated, it is just an OperationCallExp, which has a return type that tells you the horrendously complicated answer.

eclipse-qvt-oml-bot commented 5 days ago

By Christopher Gerking on Mar 10, 2016 05:01

(In reply to Ed Willink from comment #11)

Forgot to point out that while getSomeClassifierThatIsReallyADataType() may be horrendously complicated, it is just an OperationCallExp, which has a return type that tells you the horrendously complicated answer.

Does it? If the return type is EClassifier, it might return an EDataType many nested operation calls further down. That one is incompatible with an inheriting mapping of return type EClass, which is naturally a valid inheritor.

I prefer a less cumbersome syntax check, without analyzing the further control flow.

According to my experience, an inherited mapping has most often an abstract return type. Since it can't specify a reasonable result initializer, it must also become an abstract mapping without an init section:

abstract mapping m0() : EClassifier {...}

In this common case, the instantiation of the result is due to the inheriting mapping that has a concrete, instantiable return type:

mapping m1 : EClass inherits EClassifier::m0 {...}

Therefore I see no need for the execution of an inherited init section, it won't be there in most cases anyway. Especially, I see no need for a complex analysis that goes beyond the return types of the affected mappings.

eclipse-qvt-oml-bot commented 5 days ago

By Ed Willink on Mar 10, 2016 05:39

The value of OperationCallExp::type is vague in the OCL specification. Mostly it's the referredOperation's type, but if the referredOperation is a templated operation (no templates in OCL yet) such as "Set(T)::excluding(OclAny) : Set(T)", is the return type Set(T) or e.g. Set(String) reflecting the actual specialization?

I regard resolution of template specializations as a compile time activity that minimizes the amount of work needed at run time, or by other AST consumers. Therefore for the Pivot OCL there are real template specializations and the OperationCallExp::type is the specialized type making subsequent analysis as needed for Mapping init's easy.

If the Eclipse QVTo AS has inadequate precision, it may need improvement.

eclipse-qvt-oml-bot commented 5 days ago

By Adolfo Sanchez-Barbudo Herrera on Mar 10, 2016 06:47

I think that suppressing the inherited init section suppression is not OK. Who knows what any other variable or property initialization might be done, for further usage during the extended mapping execution (regardless we might argue that it's a good practice).

What Sergey and Ed are discussing makes sense to me. The problem I see is that it's contrary what the specification currently says. Might we missing a mapping inheritance scenario where the new behaviour can clash with? Could we just simply rely in our test cases to verify that the change in behaviour doesn't break any transformation ?. That said, I'm inclined to go for that: i.e. execute sections from inherited mappings (sorry I'm unclear why is bottom-up rather than top-down), then my sections.

Although I understand Christopher concern, I believe that it's just a transformation error. If we do nothing, a run-time error. If we wanted to do something, we could add a compile-time check, so that:

Regards,\ Adolfo.

eclipse-qvt-oml-bot commented 5 days ago

By Christopher Gerking on Mar 10, 2016 07:26

(In reply to Adolfo Sanchez-Barbudo Herrera from comment #14)

I think that suppressing the inherited init section suppression is not OK. Who knows what any other variable or property initialization might be done, for further usage during the extended mapping execution (regardless we might argue that it's a good practice).

Supression at runtime seems in fact counterintuitive. I'm inclined to raise a syntax error whenever an inherited mapping has an init section.

What Sergey and Ed are discussing makes sense to me. The problem I see is that it's contrary what the specification currently says.

Indeed. The specification is definitely wrong and needs to be fixed. However I see no need for a top-down execution of init sections, which is really a breaking change. An inherited mapping doesn't strictly require an init section because it doesn't instantiate the result. Therefore I see no real benefit that justifies the effort of implementing a complex analysis with probably confusing feedback to the user.

That said, I'm inclined to go for that: i.e. execute sections from inherited mappings (sorry I'm unclear why is bottom-up rather than top-down), then my sections.

It is actually top-down. I agree that this makes sense for the order of the population sections, because they serve the purpose of initializing the result's features. I see no benefit in multiple init sections, though.

eclipse-qvt-oml-bot commented 5 days ago

By Adolfo Sanchez-Barbudo Herrera on Mar 10, 2016 10:04

Hi Christopher,

Specification says:

"The init section contains some code to be executed before the instantiation of the declared outputs."

Unfortunately "some code" goes beyond initializing the result variable/s. As commented before, already working transformation might already been doing other initializations (e.g. intermediate properties) to drive/control (later on) the flow of the mapping execution. Preventing to inherit this kind of mappings, not only is a breaking change, but we would also unnecessarily adding a limitation. I can't agree with your suggestion.

Your problematic situation is genuine, but again I'm stuck to the opinion that it's just a transformation writer mistake (he probably forgot to properly initialize the result). So, as I said:

Regards,\ Adolfo.

eclipse-qvt-oml-bot commented 5 days ago

By Christopher Gerking on Mar 10, 2016 10:41

(In reply to Adolfo Sanchez-Barbudo Herrera from comment #16)

"The init section contains some code to be executed before the instantiation of the declared outputs."

Exactly. But according to the current specification, the inherited mapping is executed after the instantiation of the outputs. So there is just nothing anymore that can still be executed before.

Unfortunately "some code" goes beyond initializing the result variable/s. As commented before, already working transformation might already been doing other initializations (e.g. intermediate properties) to drive/control (later on) the flow of the mapping execution.

I doubt that this is the case, but of course what you say is completely right. We could raise the error only if the inherited mapping's init section reassigns the result. Such a behavior is definitely wrong because there is already a trace record including the prevailing result.

  • either do nothing (run-time error)

Runtime errors are bad if there is a way to avoid them.

Raising an error seems to impose another unneeded (and breaking) limitation, where some inheriting transformations (that don't have the result variable/s reassigned in your hazardous situation) might run with no run-time errors.

Can't follow you here. The error should appear on the inheriting mapping because the inherited mapping is itself locally valid. Any result assignments that are illegal from the viewpoint of the inheritor should be diagnosed as an error, no matter if they actually execute at runtime or not.

I think I made my position clear. I'm striving for a consistent solution that is implementable with a reasonable effort. I just have the feeling that changing the specification towards a top-down execution of the init sections is not profitable due to the lack of use cases. In addition it requires a form of static analysis that I don't expect to get implemented in the near future. These costs do not justify the benefits.

eclipse-qvt-oml-bot commented 5 days ago

By Ed Willink on Mar 10, 2016 11:54

Hi Christopher

Other languages regularly execute inherited functionality (inherited first /bottom-up). A VERY strong case is needed for QVTo to be different. OMG QVTo will probably clarify that inherited functionality should be executed as part of a flattened equivalent mapping and provide OCL WFRs that define what is and isn't valid at compile-time.

The only argument I see against inherited init is an implementation inconvenience. These inconveniences are always very pressing when you are close to the code. I hope you can step back and consider what is right in principle.

eclipse-qvt-oml-bot commented 5 days ago

By Christopher Gerking on Mar 11, 2016 07:41

(In reply to Ed Willink from comment #18)

Hi Christopher

Other languages regularly execute inherited functionality (inherited first /bottom-up).

Inherited first is fine. However this applies to the population sections which include the actual initialization of the existing result.

So far, no one came up with a use case of some inherited behavior that can't be executed during the population section, and therefore strictly requires an inherited init section. Even if this is a requirement, then delegation from within the inheritor's init section does the trick as well:

mapping m0() : EClassifier {\ init {\ result := object EClass{...};\ }\ }

mapping m1 : EClassifier {\ init {\ result := map m0(); // execute the inherited init section\ }\ }

Obviously this requires type compatibility with the inheritor, for example we have EClassifier as return type for both mappings in the above example.

The only argument I see against inherited init is an implementation inconvenience.

Other languages are type-safe and so is QVTo. When making assumptions about type compatibilities, they tend to rely on the declared types only. And this has proven to be right in principle. Based on your proposal, we either lose type safety (bad), or we must implement a complex control flow analysis (inconvenient). I see no need for both.

If we are going to allow this, why not applying this concept to regular operation calls as well?

helper getEClassifier() : EClassifier {...}

var x : EClass = getEClassifier();

The above call is not type-safe, but we could analyze the internal control flow of getEClassifier() to check whether all possible return values are EClasses. Very inconvenient and very directed against the concept of type safety on the basis of declared return types. What if there is a blackbox operation involved that can't be analyzed further?

eclipse-qvt-oml-bot commented 5 days ago

By Ed Willink on Mar 11, 2016 08:38

(In reply to Christopher Gerking from comment #19)

If we are going to allow this, why not applying this concept to regular operation calls as well?

helper getEClassifier() : EClassifier {...}

var x : EClass = getEClassifier();

The above call is not type-safe

and consequently a compile-time error. Unfortunately OMG QVTo has no WFRs

cf the thin and suspect OCL 8.3.7 AS WFRs

context OperationCallExp\ inv: arguments->forAll (a | a.type.conformsTo\ (self.refParams->at (arguments->indexOf (a)).type))

NB. The absence of OMG QVTo WFRs does not mean that they do not exist; just that this is yet another area that the implementer must use their common sense to remedy.

eclipse-qvt-oml-bot commented 5 days ago

By Christopher Gerking on Mar 11, 2016 11:17

(In reply to Ed Willink from comment #20)

and consequently a compile-time error.

Indeed, and that's despite the fact that the error might be unnecessarily restrictive. It is our responsibility to restrict inheritance so that the result from the inherited mapping does always conform to the inheritor's return type. One restrictive solution is to not allow an inherited init section because it might include a non-conforming reassignment of the result. Of course this may be unncessarily restrictive, but that's exactly what we do in general as well.

Maybe a best-effort approach is to allow an inherited init section, but make sure that it doesn't include a result assignment. This is easily possible without analyzing all the types, and we still support computations that must take place early (for which I see no use case, though).

eclipse-qvt-oml-bot commented 5 days ago

By Ed Willink on Mar 11, 2016 12:03

(In reply to Christopher Gerking from comment #21)

It is our responsibility to restrict inheritance so that the result from the inherited mapping does always conform to the inheritor's return type.

QVT 1.3 8.1.14.1 says:

The candidate return type must be covariant, that is the same as, or derived from that of, the disjuncting return type to ensure that no result incompatibility arises.

Since the argument types contribute to implicit predicates, the candidate parameter types may be supertypes or subtypes of the disjuncting mapping parameter type. The number of candidate and disjuncting parameter types must be the same.

Maybe a best-effort approach is to allow an inherited init section, but make sure that it doesn't include a result assignment. This is easily possible without analyzing all the types, and we still support computations that must take place early (for which I see no use case, though).

The problem arises in third party extension.

If the base mapping has an unhelpful init(), perhaps as a result of suspect programming practice, it makes extension impossible. You best-effort defines this as just tough.

I'm suggesting that an extension can erase the value but not side effects of an unhelpful init() by a replacement declaration.

So for:

mapping m1() : EClass inherits m2 {\ init {\ result := object EClass {};

}\ }\ mapping m2() : EClassifier {\ init {\ result := object EDataType {};\ }\ }

invocation of m1 successively executes the\ m2 init: result := object EDataType {}\ m1 init: result := object EClass {}

The final result is valid. The interim result is transiently valid.

Iff it can be proven that "object EDataType {}" has no side effects it may be suppressed. In general it is difficult to analyze the Java to see if there is a side effect such as an object construction counter.

eclipse-qvt-oml-bot commented 5 days ago

By Adolfo Sanchez-Barbudo Herrera on Mar 11, 2016 13:13

Hi ,

Again, I go on agreeing with Ed comments. So providing, the inherited mapping Ed introduced before:

mapping m2() : EClassifier {\ init {\ result := object EDataType {};\ } ...\ }

I discuss different scenarios for the extending mapping:

mapping m1() : EClass inherits m2 {\ init {\ result := object EClass {};

} ...\ }

I agree with Ed that this should be valid.

mapping m1() : EClassifier inherits m2 {\ // no init ...\ }

I suggested before this should be valid (output type is the same/is not narrower). We won't get a runtime error. No complicated static analysis here, just need to check if the output types are the same or not.

mapping m1() : EClass inherits m2 {

}

We would get a Run-time error. I suggested before this should be valid. Just just a warning raised to indicate that the result reassignment might be missing. We can argue if a compile-error should arise, instead. I'm in favour of warning, because if the inherited mapping were the following:

mapping m2() : EClassifier {\ init {\ result := object EClass {};\ }\ }

m1() would not get a runtime error. Therefore, we would be unnecessarily preventing the mapping extension here.


With respect to scenario 3 (the problematic scenario), is that if we don't force the inheriting mapping (which narrows the outputs) to redefine the result variable in the init section (because we simply raise a warning), we would require transitevely analyse the whole mapping inheritance, to diagnose if we have to rise a warning or not (the problematic result initialization might be in any mapping along the whole mapping inheritance). If we decided for an error, we would just need to analyse the mappings we are extending, to decide if we have to raise an error or not.

I let you follow up on this. Although I'm in favour of a warning (not a breaking change), I'm not against of raising an error ;P.

Cheers,\ Adolfo.

eclipse-qvt-oml-bot commented 5 days ago

By Adolfo Sanchez-Barbudo Herrera on Mar 11, 2016 13:18

(In reply to Adolfo Sanchez-Barbudo Herrera from comment #23)

I let you follow up on this. Although I'm in favour of a warning (not a breaking change), I'm not against of raising an error ;P.

Since we have to change (I think we have all agreed on this) the way in which init sections need to be evaluated, the no-breaking argument is kind of pointless. So, providing that we might annoy somebody, anyway, just think of the right solution:

Cheers,\ Adolfo.

eclipse-qvt-oml-bot commented 5 days ago

By Adolfo Sanchez-Barbudo Herrera on Mar 11, 2016 13:42

Final comment,

Just came up with a useful scenario were a warning (rather than an error) would be useful, at least required to support the following reusability scenario:

mapping EClassifier::m1() : EClassifier {\ init {\ result := self.giveMeAClassifier();\ }\ -- Useful reusable population section to compute some EClassifier properties\ }

mapping EClass::m2() : EClass inherits m1 {

-- Custom population section to compute some EClass properties\ }

mapping EDataType::m3() : EDataType inherits m1 {

-- Custom population section to compute some EDataType propertiees\ }

In this case, this m1, m2, and m3 mappings would correctly run, provided that the proper overloaded operations for an EClassifier::giveMeAClassifier() (returning EClasses and EDataTypes) existed in the transformation.

I think we should not get obsessed with run-time exceptions (at least, these ones). They just happen as of the OOP of the QVTo language. Just think of Java, and CCEs (Class Cast Exceptions).

Cheers,\ Adolfo.

eclipse-qvt-oml-bot commented 5 days ago

By Christopher Gerking on Sep 21, 2016 11:14

Finally returning to this one.

(In reply to Adolfo Sanchez-Barbudo Herrera from comment #23)

  • Scenario 1: Result reassigned (Ed's example) *

mapping m1() : EClass inherits m2 { init { result := object EClass {};

} ... }

I agree with Ed that this should be valid.

No immediate problem here, so I might agree. However there's no point in the init section of m2 because it plays no role whether it executes or not.

  • Scenario 3: Result not reassigned, and output is specialized *

mapping m1() : EClass inherits m2 {

}

We would get a Run-time error.

Maybe not even that. There could be a runtime check whether the result produced by m2 is compatible with the static return type of m1. If not, the implicit instantiation section of m1 could produce a new, valid result. Up to now, the implicit instantiation executes when the result is still undefined, but we could change the behavior such that it executes when the result is either undefined or incompatible.

(In reply to Adolfo Sanchez-Barbudo Herrera from comment #25)

Just came up with a useful scenario were a warning (rather than an error) would be useful, at least required to support the following reusability scenario:

If you spend slightly more lines, you get 100% type safety for free, without an error or warning:

mapping EClassifier::m1() : EClassifier {\ -- Useful reusable population section to compute some EClassifier properties\ }

mapping EClass::m2() : EClass inherits m1 {\ init {\ result := self.giveMeAClassifier(); -- known as EClass\ }\ -- Custom population section to compute some EClass properties\ }

mapping EDataType::m3() : EDataType inherits m1 {\ init {\ result := self.giveMeAClassifier(); -- known as EDataType\ }\ -- Custom population section to compute some EDataType propertiees\ }

I still think that an inherited mapping almost never requires an init section. If we are going to allow this nevertheless, I prefer to avoid any runtime errors by definition.