You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A concrete base method with a modifying override stays non-modifying; virtual callers inherit it #116
fernflower (test-oss/fernflower, main sources; maddi 8a38863c8, maddi-mod 6ed8f49f):
// ExprentpublicvoidinferExprType(VarTypeupperBound) { } // empty: a hook for subclasses// FieldExprent extends Exprent@OverridepublicvoidinferExprType(VarTypeupperBound) {
...
inferredType = ft.getSignature().type.remap(...); // modifies this
}
// AssignmentExprent extends ExprentprivateExprentleft;
@OverridepublicvoidinferExprType(VarTypeupperBounds) {
left.inferExprType(upperBounds); // virtual: left is often a FieldExprent
}
Results: FieldExprent.inferExprType modifying (right). Exprent.inferExprType has nonModifyingMethod: 1, and its implementations list includes FieldExprent.inferExprType. AssignmentExprent.inferExprType has nonModifyingMethod: 1 too, although assigning to a field calls FieldExprent.inferExprType on its own left.
So a call through a concrete, overridden method takes the base body's verdict. The fold over implementations that
abstract methods get does not seem to apply when the base method has a body. For a call whose receiver is not known
exactly, the verdict should be the base body's joined with the overrides' (within the analysed sources), here
modifying. Six classes override inferExprType in fernflower. The empty-hook-in-a-base-class shape is common enough
that this is likely not the only case.
Exprent.replaceExprent is non-modifying (the empty body), and replaceSingleVar has parent as unmodifiedParameter: 1, although its first statement replaces one of parent's operands whenever parent is anything but a bare Exprent. InvocationExprent.replaceExprent, FunctionExprent.replaceExprent and ExitExprent.replaceExprent are all modifying, and their callers through Exprent (ConcatenationHelper.simplifyStringConcat, StackVarsProcessor.iterateChildExprent) keep their parameters unmodified too.
Decision (Bart): option A. A non-modifying verdict may not stand when an override modifies. The dispatch union is now on by default; NODISPATCHUNION=1 restores the old behaviour (WIP 933606d on ws/maddi-null). TestOptimisticModificationShapes: O2-D keeps the old behaviour as the gated variant, and o2dUnion pins the default. Corpus A/B pending.
Fixed on devel (merge 00b2a40) as decided (option A: a @notmodified verdict is not overridden by a modifying override; the base is the union over its implementations): 933606d (dispatch union on by default, gate NODISPATCHUNION). Gate green on devel after the merge (laser1, 2026-11-13).
Activity
bnaudts commentedon Nov 12, 2026
A stronger case of the same,
Exprent.replaceExprent:Exprent.replaceExprentis non-modifying (the empty body), andreplaceSingleVarhasparentasunmodifiedParameter: 1, although its first statement replaces one ofparent's operands wheneverparentis anything but a bareExprent.InvocationExprent.replaceExprent,FunctionExprent.replaceExprentandExitExprent.replaceExprentare all modifying, and their callers throughExprent(ConcatenationHelper.simplifyStringConcat,StackVarsProcessor.iterateChildExprent) keep their parameters unmodified too.🤖 Generated with Claude Code
bnaudts commentedon Nov 13, 2026
Decision (Bart): option A. A non-modifying verdict may not stand when an override modifies. The dispatch union is now on by default;
NODISPATCHUNION=1restores the old behaviour (WIP 933606d on ws/maddi-null).TestOptimisticModificationShapes: O2-D keeps the old behaviour as the gated variant, ando2dUnionpins the default. Corpus A/B pending.WIP #116: dispatch union on by default (gate NODISPATCHUNION)
Merge ws/maddi-null up to b0050ed: #112-#116, #119
bnaudts commentedon Nov 13, 2026
Fixed on devel (merge 00b2a40) as decided (option A: a @notmodified verdict is not overridden by a modifying override; the base is the union over its implementations): 933606d (dispatch union on by default, gate NODISPATCHUNION). Gate green on devel after the merge (laser1, 2026-11-13).