daniellansun commented on PR #2736:
URL: https://github.com/apache/groovy/pull/2736#issuecomment-5106124863

   @blackdrag Thank you for pressing on the “when / why” of hierarchy fan-out. 
That is the right question, and the previous reply was still too coarse. Below 
is the refined model after re-checking MOP selection against your scenario.
   
   ## 1. When we invalidate (and when we do not)
   
   Monomorphic indy sites guard a **class-domain** SwitchPoint on the 
*receiver’s* runtime class (via `ClassInfo`). They re-link only when *that* 
domain is retired.
   
   For your ladder `A extends B extends C` and `call(C x) { x.fooC() }` with `c 
= new C()`:
   
   | Operation | Retires `C`’s domain? | `foo*` visible on `c` / `C.metaClass`? 
|
   |-----------|----------------------|----------------------------------------|
   | `B.metaClass = EMC` + `fooB0` | **No** (`C` is a *supertype* of `B`) | 
**No** |
   | `B.metaClass = null` | **No** | **No** |
   | `enableEMCglobally()` | Only if a bulk path fires; not via typed `B` 
change | Global EMC changes *creation*, not `C`’s linked target by itself |
   | `c.metaClass = null` | Per-instance path → exact `C` only if a class-level 
SP was tied to that path | Restores class-level MC for `c` |
   | `B.metaClass = new MetaClassImpl()` | **No** for pure `MetaClassImpl` 
(exact `B` only) | **No** |
   | Detached EMC `fooB1` after replace | Depends on whether that EMC is still 
the registered MC | **No** on `c` |
   | `A.fooA = {}` | **No** for `C` | **No** on `c` |
   | Per-instance EMC on `b` + `fooB2` | **No** for `C` | **No** on `c` |
   
   So for `call(c)` / `fooC`, the new scoped model already keeps `C`’s site 
linked across the operations that only touch `B`/`A`. That is the main 
behavioural difference vs the old process-wide SwitchPoint (which retired 
*everything*).
   
   Regression coverage for this matrix is in 
`Groovy12191.blackdragScenario_callOnC_notInvalidatedBySubclassMetaClassChurn`.
   
   ## 2. Why hierarchy exists at all (and when it does not)
   
   Hierarchy fan-out is **not** “`MetaClassImpl` shares one table up the 
hierarchy”. Each class still has its own MetaClass / `ClassInfo` domain.
   
   It exists because **selection** can observe *ancestor* MetaClass state:
   
   - `MetaClassImpl.findMethodInClassHierarchy` only opens a real walk when 
some strong MetaClass in the hierarchy is a **modified** `MutableMetaClass` 
(EMC is the common case).
   - Empirically, `Parent.metaClass.hello = {…}` is visible on `new 
Child().hello()` **without** `enableGlobally()`.
   - Empirically, `Object[].metaClass.arrHello = {…}` is visible on `String[]` 
— that is why the array lattice is indexed.
   
   So hierarchy is required for **cross-class MOP visibility**, not for 
ordinary `MetaClassImpl` method tables.
   
   ### Refined policy (exact-class is an allow-list)
   
   | Event | Width |
   |-------|--------|
   | EMC install / remove / in-place update (`incVersion`) | class + loaded 
subtypes |
   | Global EMC creation handle active | class + hierarchy |
   | Interface or array MetaClass change | class + hierarchy (array lattice) |
   | Modified custom `MetaClassImpl` subclass (`isModified()==true`) | class + 
hierarchy |
   | Pure unmodified `MetaClassImpl` ↔ `MetaClassImpl` (or null) | **exact 
class only** |
   | Per-instance MetaClass change | **exact class only** |
   | Category enter/leave, unscoped registry event | all loaded class domains |
   
   This matches your intuition: **without EMC (and without a modified mutable 
MetaClass), hierarchy is unnecessary**. Pure `MetaClassImpl` parent replace no 
longer fans out to children. Unknown/custom MetaClass kinds still fan out 
(correctness-first).
   
   ## 3. “Should the MetaClass own the SwitchPoint?”
   
   Agreed in principle. The current class-domain SP is a **stand-in for 
class-level MetaClass generation**, not a claim that every MetaClass kind 
shares the same invalidation logic.
   
   A MetaClass-owned guard would be the natural place for:
   
   - MetaClass-specific update vs replace semantics,
   - true per-instance domains (today: uncacheable PIC + exact class retire),
   - custom MetaClass implementations that do not fit EMC / `MetaClassImpl`.
   
   That is a larger redesign than this PR. For 6.0 we kept a single monomorphic 
hot-path guard and made invalidation **MetaClass-aware** at the registry 
boundary. MetaClass-owned SwitchPoints remain a follow-up once the “when / why” 
matrix above is agreed.
   
   ## 4. Update vs replace, and `version`
   
   You are right that parent method *addition* is often an **update** of the 
same MetaClass, not a registry replace. That path is `ClassInfo.incVersion()` 
(from EMC), which still does hierarchy fan-out — because subtype sites may have 
linked a miss (or an older target) and must re-select to see the new expando 
method.
   
   Per-instance MetaClass continues to use the existing uncacheable PIC path 
(`canSetTarget == false`); we do not introduce per-instance SwitchPoints in 
this PR.
   
   ## 5. PIC sentinel rename
   
   Per 
[discussion_r3664025383](https://github.com/apache/groovy/pull/2736#discussion_r3664025383):
   
   - `NULL_METHOD_HANDLE_WRAPPER` → `UNCACHEABLE_PIC_SENTINEL`
   - meaning: “do not store this receiver shape under a class-keyed PIC entry”
   - deprecated alias kept as `getNullMethodHandleWrapper()` for in-tree 
callers during the rename
   
   ## 6. What we are *not* claiming
   
   - We do **not** claim minimal invalidation at method-name granularity 
(`fooC` unchanged ⇒ never re-link). The monomorphic SwitchPoint still retires 
the whole class domain when that class’s MOP generation changes — same shape as 
pre-6.0, but **scoped**.
   - We do **not** claim MetaClass-owned SwitchPoints are done; only that 
policy is now MetaClass-aware on top of class domains.
   
   Happy to adjust further if you want the exact-only allow-list tightened or 
loosened (e.g. EMC-only fan-out even for interfaces/arrays without EMC).
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to