Skip to content

ST6RI-925 Problem with deriveTypeFeatureMembership (KERML11-191)#749

Merged
seidewitz merged 4 commits intomasterfrom
ST6RI-925
Apr 2, 2026
Merged

ST6RI-925 Problem with deriveTypeFeatureMembership (KERML11-191)#749
seidewitz merged 4 commits intomasterfrom
ST6RI-925

Conversation

@seidewitz
Copy link
Copy Markdown
Member

This PR proactively implements the resolution to the following issue, which the KerML 1.1 RTF has elevated to be a "blocker" issue.

Background

Currently (as of KerML 1.0), the OCL for deriveTypeFeatureMembership is

featureMembership = ownedFeatureMembership->union(
    inheritedMembership->selectByKind(FeatureMembership))

The intent here is clearly to not include imported feature memberships. The problem is that inheritedMembership does include imported feature memberships from supertypes. Since Type::feature is derived from featureMembership, this means that a type may get features that are not owned features of any of its supertypes, which may have owning types incompatible with the inheriting type.

Overview

The proposed change to the OCL is to simply further filter the inherited feature memberships with select(mem | self.specializes(mem.owningType)). The implementation in Type_featureMembership_SettingDelegate could be similarly updated with such a filter. However, doing this naively has a notable negative effect on performance. This is because specializes is implemented by computing the allSupertypes transitive closure and then checking against that. And the set of allSupertypes gets recomputed on each call to specializes.

The performance problem can be mitigated by computing allSupertypes once, outside the filtering loop, and then using allSupertypes.contains rather than this.specializes. With this change (and caching), the performance is essentially effected by the change.

Changes

  • Type_featureMembership_SettingDelegate – Revised basicGetto simply call TypeUtil.getFeatureMembershipOf.
  • TypeUtil – Added getFeatureMembershipOf to provide access to TypeAdapter.getFeatureMembership.
  • TypeAdapter – Implemented getFeatureMembership as discussed above. Implementing this in the adapter allows the computed featureMembership to be cached (alongside inheritedMembership).

- The featureMemberships of a type should only include
inheritedMemberships whose owningType is a direct or indirect supertype.
- Added filter that owningType is a supertype.
- There is a slight performance degradation.
@seidewitz seidewitz self-assigned this Apr 1, 2026
@seidewitz seidewitz added this to the 2026-02 milestone Apr 1, 2026
@seidewitz seidewitz requested a review from himi April 1, 2026 21:19
@himi
Copy link
Copy Markdown
Member

himi commented Apr 2, 2026

It tool much time to understand what the critical issue, but now I think I can understand. We need to properly filter featureMembership to keep type compatibility. And I did not notice import memberships are inherited! So I tested it with:

package Test {
    part def A {
        part f;
    }
    part def Bprivate {
        private import A::*;
        part g;
    }
    part def Bpublic {
        public import A::*;
        part g;
    }
    part def Bprotected {
        protected import A::*;
        part g;
    }
    part def Cprivate :> Bprivate;
    part def Cpublic :> Bpublic;
    part def Cprotected :> Bprotected;
}

And get owningType of thier featureMembership with the following code:

from mgpy.mg import iMg
def showFeatureMembership(q):
    C = iMg.dq(q)[0]
    print(f'{C.qualifiedName}')
    for ms in C.featureMembership:
        e = ms.memberElement
        if not e.isStandardLibrary:
            print(f'  {e.qualifiedName} owningType: {ms.owningType.qualifiedName}')

showFeatureMembership('Test.A')
showFeatureMembership('Test.Bpublic')
showFeatureMembership('Test.Bprivate')
showFeatureMembership('Test.Bprotected')
showFeatureMembership('Test.Cprivate')
showFeatureMembership('Test.Cpublic')
showFeatureMembership('Test.Cprotected')

And then I obtained:

Test::A
  Test::A::f owningType: Test::A
Test::Bpublic
  Test::Bpublic::g owningType: Test::Bpublic
Test::Bprivate
  Test::Bprivate::g owningType: Test::Bprivate
Test::Bprotected
  Test::Bprotected::g owningType: Test::Bprotected
Test::Cprivate
  Test::Bprivate::g owningType: Test::Bprivate
Test::Cpublic
  Test::Bpublic::g owningType: Test::Bpublic
Test::Cprotected
  Test::Bprotected::g owningType: Test::Bprotected

They are correct!

@himi
Copy link
Copy Markdown
Member

himi commented Apr 2, 2026

Just FYI, before this fix, I got:

Test::A
  Test::A::f owningType: Test::A
Test::Bpublic
  Test::Bpublic::g owningType: Test::Bpublic
Test::Bprivate
  Test::Bprivate::g owningType: Test::Bprivate
Test::Bprotected
  Test::Bprotected::g owningType: Test::Bprotected
Test::Cprivate
  Test::Bprivate::g owningType: Test::Bprivate
Test::Cpublic
  Test::Bpublic::g owningType: Test::Bpublic
  Test::A::f owningType: Test::A
Test::Cprotected
  Test::Bprotected::g owningType: Test::Bprotected
  Test::A::f owningType: Test::A

@seidewitz seidewitz merged commit e471a32 into master Apr 2, 2026
1 check failed
seidewitz added a commit that referenced this pull request Apr 2, 2026
ST6RI-925 Problem with deriveTypeFeatureMembership (KERML11-191)
@seidewitz seidewitz modified the milestones: 2026-02, 2026-03 Apr 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants