From dcaf8a5fbb99581273daf6d8d60d2b72858f097e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 02:48:22 +0000 Subject: [PATCH 1/2] refactor(runtime): clear the Sonar findings in internal/exec/runtime Split evalFeatureChain, recordedOccurrences and bindingWitnesses into named helpers, pass the binding site of a calc invocation as one value, take the connector fill's own undo instead of a placeholder closure, name the journal nothing opened, and tidy four tests (opposite operator, a shadowed copy, two counts now reported). Co-Authored-By: jason.han --- .../binding_connector_multiplicity_test.go | 2 +- internal/exec/runtime/calc_usage.go | 2 +- internal/exec/runtime/connector.go | 17 ++- internal/exec/runtime/eval.go | 110 +++++++++------ internal/exec/runtime/held_image.go | 110 ++++++++------- internal/exec/runtime/invoke_calc.go | 25 ++-- internal/exec/runtime/invoke_predicate.go | 2 +- internal/exec/runtime/namespace_binding.go | 2 +- internal/exec/runtime/perform.go | 2 +- .../robustness_loop_performances_test.go | 2 +- .../runtime/robustness_value_stand_in_test.go | 4 +- .../exec/runtime/shape_items_limits_test.go | 2 +- internal/exec/runtime/subsetting.go | 130 ++++++++++-------- 13 files changed, 245 insertions(+), 165 deletions(-) diff --git a/internal/exec/runtime/binding_connector_multiplicity_test.go b/internal/exec/runtime/binding_connector_multiplicity_test.go index 32dfc9fd00..998db69d76 100644 --- a/internal/exec/runtime/binding_connector_multiplicity_test.go +++ b/internal/exec/runtime/binding_connector_multiplicity_test.go @@ -37,7 +37,7 @@ func TestConnectorMultiplicityBoundsLinks(t *testing.T) { t.Fatalf("%s = %v, want what its own declaration states", expr, err) } if got := len(elementsOf(val)); got != want { - t.Errorf("%s = %s, want %d object(s) of its own", expr, FormatValue(val), want) + t.Errorf("%s = %s holds %d object(s), want %d of its own", expr, FormatValue(val), got, want) } } // `many : Real [2]` states no value, so the binding to `own` leaves it undetermined. diff --git a/internal/exec/runtime/calc_usage.go b/internal/exec/runtime/calc_usage.go index f0bb6b14f0..b7c4c5e1f8 100644 --- a/internal/exec/runtime/calc_usage.go +++ b/internal/exec/runtime/calc_usage.go @@ -904,7 +904,7 @@ func (ctx *Context) bindCalcUsage(shape *calcShape, reader *EvalContext, args ca if shape.readsOpen(nested, args) { open = unsupplied } - if err := ctx.bindCalcParameters(shape, ec, args, reader.scope, env, nested, occurrence, open); err != nil { + if err := ctx.bindCalcParameters(shape, ec, args, reader.scope, env, calcBindingSite{nested: nested, occurrence: occurrence, open: open}); err != nil { if ec.trace != nil { ec.trace.recordCalculationExitError(shape.Kind, shape.Name, err) } diff --git a/internal/exec/runtime/connector.go b/internal/exec/runtime/connector.go index 699c66f741..f193eda945 100644 --- a/internal/exec/runtime/connector.go +++ b/internal/exec/runtime/connector.go @@ -84,7 +84,7 @@ func (ctx *Context) materializeConnectorCollection(owner *Instance, fv *FeatureV mark := len(ctx.created) commit, rollback := ctx.beginJournal() endBoundary := ctx.beginRunBoundary() - undoFills := func() {} + filled, undoFills, err := ctx.fillOptionalConnectorSubsetters(owner, name, int(count)) fail := func(err error) error { fv.Values, fv.Materialized = Value{}, false undoFills() @@ -93,12 +93,10 @@ func (ctx *Context) materializeConnectorCollection(owner *Instance, fv *FeatureV release() return err } - elements := append(make([]Value, 0, int64(len(contributed))+count), contributed...) - filled, undo, err := ctx.fillOptionalConnectorSubsetters(owner, name, int(count)) - undoFills = undo if err != nil { return fail(err) } + elements := append(make([]Value, 0, int64(len(contributed))+count), contributed...) conns := append(make([]*Instance, 0, count), filled...) for _, conn := range filled { elements = append(elements, Value{Kind: ValInstance, Instance: conn.ID}) @@ -359,13 +357,22 @@ func (ctx *Context) materializeConnectorStarting(owner *Instance, connSym, base return ctx.runAttachedBehaviors() } +// noJournal stands for a journal nothing opened: committing or rolling back has nothing to +// make durable or undo, and no behavior was woken that a drain could let answer. +func noJournal() (commit, rollback func(), drain func() error) { + nothing := func() { + // Nothing was opened, so there is nothing to commit or undo. + } + return nothing, nothing, func() error { return nil } +} + // connectorJournal opens a journal and a run boundary when sym is a connector usage, so what a // namespace path does for it — attach, record, start — is undone whole on failure as a nested // connector's is. commit makes it durable; drain, once the path's own guards are cleared, lets the // older behaviors it woke answer, a failure of theirs their own. For any other usage all are no-ops. func (ctx *Context) connectorJournal(sym *symbols.Symbol) (commit, rollback func(), drain func() error) { if !ctx.model.semantics.IsConnectorObjectUsage(sym) { - return func() {}, func() {}, func() error { return nil } + return noJournal() } endBoundary := ctx.beginRunBoundary() commitJournal, rollbackJournal := ctx.beginJournal() diff --git a/internal/exec/runtime/eval.go b/internal/exec/runtime/eval.go index 036f40ff5e..d6f36a192a 100644 --- a/internal/exec/runtime/eval.go +++ b/internal/exec/runtime/eval.go @@ -1616,14 +1616,8 @@ func (ec *EvalContext) evalFeatureChain(n *ast.FeatureChainExpr) (Value, error) // A constraint usage of the object being evaluated is the predicate it enacts, // read through the object (`fits.limit` as `self.fits.limit`). - if name := simpleEndName(base); name != "" && ec.self != nil { - if _, enacted := ec.enactedConstraint(ec.self, name); enacted { - if names, err := ec.namesValue(name); err != nil { - return Value{}, err - } else if !names { - return ec.chainMemberValue(Value{Kind: ValInstance, Instance: ec.self.ID}, append([]ast.NameSegment{{Text: name}}, parts...), "") - } - } + if val, ok, err := ec.enactedConstraintChain(base, parts); ok { + return val, err } // A variant named through the variation usage it belongs to is that variant, @@ -1639,48 +1633,83 @@ func (ec *EvalContext) evalFeatureChain(n *ast.FeatureChainExpr) (Value, error) if sym, ok, err := ec.occurrenceOperand(base); err != nil { return Value{}, err } else if ok { - // A usage of the behavior performed is read from its performance, so `fresh.n` - // inside `go` reads the part the performance holds, as a bare `fresh` does. - if val, ok, err := ec.performanceFeatureOf(sym); ok { - if err != nil { - return Value{}, err - } - return ec.chainMemberValue(val, parts, sym.Name) - } - // A usage of an enclosing object is read from that object, so a sibling - // chain `e1.length` inside `e3` reads the containing rectangle's e1. - if val, ok, err := ec.outerFeatureValue(sym); ok { - if err != nil { - return Value{}, err - } - return ec.chainMemberValue(val, parts, sym.Name) - } - // The objects read as their values, whose own members they answer before the objects'. - val, err := ec.ctx.denotedValue(sym) - if err != nil { - return Value{}, err - } - return ec.chainMemberValue(val, parts, sym.Name) + return ec.occurrenceChainValue(sym, parts) } // One chain is one read from its first operand through every member in turn // (KerML 1.0 §7.4.9.3), not a read off the value of a shorter chain. operand, err := ec.Eval(base) if err != nil { - var noValue *NoValueError - if errors.As(err, &noValue) { - if unresolved := ec.chainMembersDeclared(base, parts); unresolved != nil { - return Value{}, unresolved - } - // A usage no object stands for answers the chain through the - // value its member declares, as `usage::member` reads it. - if val, ok, err := ec.declaredChainValue(base, parts); ok { - return val, err - } + return ec.valuelessChain(err, base, parts) + } + return ec.operandChainValue(operand, base, parts) +} + +// enactedConstraintChain reads a chain whose base names a constraint usage the +// object being evaluated enacts through that object, unless the name has a value. +func (ec *EvalContext) enactedConstraintChain(base ast.Node, parts []ast.NameSegment) (Value, bool, error) { + name := simpleEndName(base) + if name == "" || ec.self == nil { + return Value{}, false, nil + } + if _, enacted := ec.enactedConstraint(ec.self, name); !enacted { + return Value{}, false, nil + } + if names, err := ec.namesValue(name); err != nil { + return Value{}, true, err + } else if names { + return Value{}, false, nil + } + val, err := ec.chainMemberValue(Value{Kind: ValInstance, Instance: ec.self.ID}, append([]ast.NameSegment{{Text: name}}, parts...), "") + return val, true, err +} + +// occurrenceChainValue reads the chain's members from the object sym denotes. +func (ec *EvalContext) occurrenceChainValue(sym *symbols.Symbol, parts []ast.NameSegment) (Value, error) { + // A usage of the behavior performed is read from its performance, so `fresh.n` + // inside `go` reads the part the performance holds, as a bare `fresh` does. + if val, ok, err := ec.performanceFeatureOf(sym); ok { + if err != nil { + return Value{}, err + } + return ec.chainMemberValue(val, parts, sym.Name) + } + // A usage of an enclosing object is read from that object, so a sibling + // chain `e1.length` inside `e3` reads the containing rectangle's e1. + if val, ok, err := ec.outerFeatureValue(sym); ok { + if err != nil { + return Value{}, err } + return ec.chainMemberValue(val, parts, sym.Name) + } + // The objects read as their values, whose own members they answer before the objects'. + val, err := ec.ctx.denotedValue(sym) + if err != nil { return Value{}, err } + return ec.chainMemberValue(val, parts, sym.Name) +} +// valuelessChain answers a chain whose base has no value: unresolved when a +// member is undeclared, the declared member value when there is one, else err. +func (ec *EvalContext) valuelessChain(err error, base ast.Node, parts []ast.NameSegment) (Value, error) { + var noValue *NoValueError + if !errors.As(err, &noValue) { + return Value{}, err + } + if unresolved := ec.chainMembersDeclared(base, parts); unresolved != nil { + return Value{}, unresolved + } + // A usage no object stands for answers the chain through the + // value its member declares, as `usage::member` reads it. + if val, ok, err := ec.declaredChainValue(base, parts); ok { + return val, err + } + return Value{}, err +} + +// operandChainValue reads the chain's members from the value its base evaluated to. +func (ec *EvalContext) operandChainValue(operand Value, base ast.Node, parts []ast.NameSegment) (Value, error) { if operand.Kind == ValInstance { if _, ok := ec.ctx.instances[operand.Instance]; !ok { return Value{}, fmt.Errorf("instance ID %d not found", operand.Instance) @@ -1700,7 +1729,6 @@ func (ec *EvalContext) evalFeatureChain(n *ast.FeatureChainExpr) (Value, error) return val, err } } - return ec.chainMemberValue(operand, parts, "") } diff --git a/internal/exec/runtime/held_image.go b/internal/exec/runtime/held_image.go index 305e1a071a..d70305dc28 100644 --- a/internal/exec/runtime/held_image.go +++ b/internal/exec/runtime/held_image.go @@ -1089,70 +1089,88 @@ func (m *materializing) message(msg Message) (Message, error) { // recordedOccurrences brings the image's shots, fired successions and transfers // to dst under the identities the objects were made with. func (m *materializing) recordedOccurrences() error { - made := func(id int64) int64 { - if inst, ok := m.made[id]; ok { - return inst.ID - } - return 0 - } - madeAll := func(ids []int64) []int64 { - out := make([]int64, 0, len(ids)) - for _, id := range ids { - if to := made(id); to != 0 { - out = append(out, to) - } - } - return out - } for id, values := range m.img.startValues { - to := made(id) + to := m.madeID(id) if to == 0 { continue } - carried := make(map[string]Value, len(values)) - for name, value := range values { - value, err := m.value(value) - if err != nil { - return fmt.Errorf("object #%d start value %s: %w", id, name, err) - } - carried[name] = value + carried, err := m.carriedStartValues(id, values) + if err != nil { + return err } m.dst.startValues[to] = carried } for id, shots := range m.img.shots { - if to := made(id); to != 0 { - m.dst.shots[to] = occurrenceShots{start: made(shots.start), end: made(shots.end)} + if to := m.madeID(id); to != 0 { + m.dst.shots[to] = occurrenceShots{start: m.madeID(shots.start), end: m.madeID(shots.end)} } } for id, predecessors := range m.img.fired { - if to := made(id); to != 0 { - m.dst.fired[to] = madeAll(predecessors) + if to := m.madeID(id); to != 0 { + m.dst.fired[to] = m.madeIDs(predecessors) } } for _, record := range m.img.transfers { - to := made(record.id) + to := m.madeID(record.id) if to == 0 || slices.ContainsFunc(m.dst.transfers, func(r transferRecord) bool { return r.id == to }) { continue } - payload := record.payload - if elems := elementsOf(payload); len(elems) > 0 { - mapped := make([]Value, 0, len(elems)) - for _, elem := range elems { - if elem.Kind == ValInstance { - elem = Value{Kind: ValInstance, Instance: made(elem.Instance)} - } - mapped = append(mapped, elem) - } - if payload.Kind == ValInstance { - payload = mapped[0] - } else { - payload = sequenceOf(mapped) + m.dst.transfers = append(m.dst.transfers, m.carriedTransfer(to, record)) + } + return nil +} + +// madeID is the identity the image's object id was made under, 0 when it was not made. +func (m *materializing) madeID(id int64) int64 { + if inst, ok := m.made[id]; ok { + return inst.ID + } + return 0 +} + +// madeIDs maps ids to the identities they were made under, dropping those not made. +func (m *materializing) madeIDs(ids []int64) []int64 { + out := make([]int64, 0, len(ids)) + for _, id := range ids { + if to := m.madeID(id); to != 0 { + out = append(out, to) + } + } + return out +} + +// carriedStartValues brings the start values recorded for the image's object id to dst. +func (m *materializing) carriedStartValues(id int64, values map[string]Value) (map[string]Value, error) { + carried := make(map[string]Value, len(values)) + for name, value := range values { + value, err := m.value(value) + if err != nil { + return nil, fmt.Errorf("object #%d start value %s: %w", id, name, err) + } + carried[name] = value + } + return carried, nil +} + +// carriedTransfer is record for the transfer made as to, its ends and payload mapped alike. +func (m *materializing) carriedTransfer(to int64, record transferRecord) transferRecord { + payload := record.payload + if elems := elementsOf(payload); len(elems) > 0 { + mapped := make([]Value, 0, len(elems)) + for _, elem := range elems { + if elem.Kind == ValInstance { + elem = Value{Kind: ValInstance, Instance: m.madeID(elem.Instance)} } + mapped = append(mapped, elem) + } + if payload.Kind == ValInstance { + payload = mapped[0] + } else { + payload = sequenceOf(mapped) } - m.dst.transfers = append(m.dst.transfers, transferRecord{ - id: to, source: made(record.source), target: made(record.target), payload: payload, - sources: madeAll(record.sources), targets: madeAll(record.targets), - }) } - return nil + return transferRecord{ + id: to, source: m.madeID(record.source), target: m.madeID(record.target), payload: payload, + sources: m.madeIDs(record.sources), targets: m.madeIDs(record.targets), + } } diff --git a/internal/exec/runtime/invoke_calc.go b/internal/exec/runtime/invoke_calc.go index 1da6fd66c0..45d36d0d94 100644 --- a/internal/exec/runtime/invoke_calc.go +++ b/internal/exec/runtime/invoke_calc.go @@ -835,7 +835,7 @@ func (ctx *Context) invokeCalcShapeDirect(shape *calcShape, args calcArgs, calle ec.trace.recordCalculationEnter(shape.Kind, shape.Name) } - if err := ctx.bindCalcParameters(shape, ec, args, callerScope, locals, nil, occurrence, nil); err != nil { + if err := ctx.bindCalcParameters(shape, ec, args, callerScope, locals, calcBindingSite{occurrence: occurrence}); err != nil { if ec.trace != nil { ec.trace.recordCalculationExitError(shape.Kind, shape.Name, err) } @@ -923,23 +923,30 @@ func (ctx *Context) leaveCalc() { ctx.calcDepth-- } +// calcBindingSite is where an invocation's parameters are bound beyond the call +// itself: nested, when non-null, is the environment reading a nested usage, which +// the bindings the usage itself declares are written in and evaluate against; +// occurrence is the performance the bound values are also written to; open names +// the parameters a usage read as a feature leaves unsupplied, bound to no value. +type calcBindingSite struct { + nested *EvalContext + occurrence *calcOccurrence + open map[string]bool +} + // bindCalcParameters binds the calc's input parameters into bindings: each // parameter's argument, or, for a parameter no argument supplies, the default // declared closest to the invoked calc, evaluated in the scope of the calc -// declaring it. ec supplies the environment defaults are evaluated in; nested, -// when non-null, is the environment reading a nested usage, which the bindings -// the usage itself declares are written in and evaluate against. open names the -// parameters a usage read as a feature leaves unsupplied, bound to no value. +// declaring it. ec supplies the environment defaults are evaluated in. func (ctx *Context) bindCalcParameters( shape *calcShape, ec *EvalContext, args calcArgs, callerScope *symbols.Scope, bindings frame, - nested *EvalContext, - occurrence *calcOccurrence, - open map[string]bool, + site calcBindingSite, ) error { + nested, occurrence, open := site.nested, site.occurrence, site.open positional := 0 for i := range shape.Params { param := &shape.Params[i] @@ -1482,7 +1489,7 @@ func (ctx *Context) applyLibraryPerformance(perf *libraryPerformance, args calcA trace: ctx.trace, activation: activation, } - if err := ctx.bindCalcParameters(sig, ec, args, callerScope, locals, nil, nil, nil); err != nil { + if err := ctx.bindCalcParameters(sig, ec, args, callerScope, locals, calcBindingSite{}); err != nil { return Value{}, err } diff --git a/internal/exec/runtime/invoke_predicate.go b/internal/exec/runtime/invoke_predicate.go index 7bf01e987d..52aecd7ca7 100644 --- a/internal/exec/runtime/invoke_predicate.go +++ b/internal/exec/runtime/invoke_predicate.go @@ -137,7 +137,7 @@ func (ec *EvalContext) invokePredicateDirect(sym *symbols.Symbol, args calcArgs) if binder.trace != nil { binder.trace.recordCalculationEnter(shape.Kind, shape.Name) } - if err := ctx.bindCalcParameters(shape, binder, args, ec.scope, env, nil, nil, nil); err != nil { + if err := ctx.bindCalcParameters(shape, binder, args, ec.scope, env, calcBindingSite{}); err != nil { if binder.trace != nil { binder.trace.recordCalculationExitError(shape.Kind, shape.Name, err) } diff --git a/internal/exec/runtime/namespace_binding.go b/internal/exec/runtime/namespace_binding.go index a7973d4088..65c1549da2 100644 --- a/internal/exec/runtime/namespace_binding.go +++ b/internal/exec/runtime/namespace_binding.go @@ -673,7 +673,7 @@ func (ctx *Context) namespacedSubsetObjects(sym *symbols.Symbol) ([]*Instance, b mult := ctx.featureMultiplicity(sym, ctx.findOwnerType(sym)) abstract := symbols.IsAbstract(sym) - commit, rollback, drain := func() {}, func() {}, func() error { return nil } + commit, rollback, drain := noJournal() if !abstract { count, err := ctx.lowerBoundCount(mult, len(contributed), fmt.Sprintf("usage %s", SymbolText(sym))) if err != nil { diff --git a/internal/exec/runtime/perform.go b/internal/exec/runtime/perform.go index e4d1e8aaa2..d22f53cb36 100644 --- a/internal/exec/runtime/perform.go +++ b/internal/exec/runtime/perform.go @@ -276,7 +276,7 @@ func (e *StateExecutor) callInputs(member *symbols.Symbol, operation string, arg } bound := mapFrame(make(map[string]Value, len(shape.Params))) ec.frames = []frame{bound} - if err := ctx.bindCalcParameters(shape, ec, calcArgs{named: inputs}, scope, bound, nil, nil, nil); err != nil { + if err := ctx.bindCalcParameters(shape, ec, calcArgs{named: inputs}, scope, bound, calcBindingSite{}); err != nil { return nil, err } return bound.vars, nil diff --git a/internal/exec/runtime/robustness_loop_performances_test.go b/internal/exec/runtime/robustness_loop_performances_test.go index cd994c61e8..0918783f06 100644 --- a/internal/exec/runtime/robustness_loop_performances_test.go +++ b/internal/exec/runtime/robustness_loop_performances_test.go @@ -91,7 +91,7 @@ func testLoopPerformancesEndWithTheRun(t *testing.T) { for i := 1; i < len(bodies); i++ { before, _ := ctx.OccurrenceLife(mustObject(bodies[i-1])) after, _ := ctx.OccurrenceLife(mustObject(bodies[i])) - if !(before.Ended <= after.Began) { + if before.Ended > after.Began { t.Errorf("body #%d ended at %d after body #%d began at %d: iterations are in sequence", i, before.Ended, i+1, after.Began) } } diff --git a/internal/exec/runtime/robustness_value_stand_in_test.go b/internal/exec/runtime/robustness_value_stand_in_test.go index adbf41cce6..7f25e3eb26 100644 --- a/internal/exec/runtime/robustness_value_stand_in_test.go +++ b/internal/exec/runtime/robustness_value_stand_in_test.go @@ -179,8 +179,8 @@ func TestRuntimeRobustnessValueStandIn(t *testing.T) { t.Fatalf("stand-in #%d is still registered after the write", standIn) } copied := imageInto(t, ctx, host) - if source, copy := heldDigest(ctx, everyObject), heldDigest(copied, everyObject); source != copy { - t.Fatalf("the copy differs from the source:\n--- source\n%s\n--- copy\n%s", source, copy) + if source, replica := heldDigest(ctx, everyObject), heldDigest(copied, everyObject); source != replica { + t.Fatalf("the copy differs from the source:\n--- source\n%s\n--- copy\n%s", source, replica) } }) diff --git a/internal/exec/runtime/shape_items_limits_test.go b/internal/exec/runtime/shape_items_limits_test.go index f5de3a63bc..22ba875c69 100644 --- a/internal/exec/runtime/shape_items_limits_test.go +++ b/internal/exec/runtime/shape_items_limits_test.go @@ -51,7 +51,7 @@ func TestShapeItemsUnsupportedExpressionsAreTypedErrors(t *testing.T) { t.Fatalf("%s: %v", expr, err) } if got := len(elementsOf(val)); got != want { - t.Errorf("%s = %s, want %d objects", expr, FormatValue(val), want) + t.Errorf("%s = %s holds %d objects, want %d", expr, FormatValue(val), got, want) } } diff --git a/internal/exec/runtime/subsetting.go b/internal/exec/runtime/subsetting.go index de915dff14..ad0fe6a2b6 100644 --- a/internal/exec/runtime/subsetting.go +++ b/internal/exec/runtime/subsetting.go @@ -5,6 +5,7 @@ import ( "math" "slices" + "github.com/Open-MBEE/OpenSysML/internal/ir/lower" "github.com/Open-MBEE/OpenSysML/internal/semantic/semantics" "github.com/Open-MBEE/OpenSysML/internal/semantic/symbols" "github.com/Open-MBEE/OpenSysML/internal/syntax/ast" @@ -1111,67 +1112,16 @@ func (ctx *Context) siblingSubsetterValues(inst *Instance, fv *FeatureValue, sub // bindingWitnesses draws, per binding constraining the named feature, as many of its other end's // values as the binding links, from pool where the subsetted features fix one (KerML 1.0 §7.4.6). func (ctx *Context) bindingWitnesses(inst *Instance, name string, pool []Value, pooled bool, taken map[int64]bool) ([]Value, map[int64]bool, error) { - fv := inst.FeatureValues[name] - var chosen []Value - var seen map[int64]bool - inPool := map[int64]bool{} + draw := &witnessDraw{fv: inst.FeatureValues[name], pooled: pooled, inPool: make(map[int64]bool, len(pool)), taken: taken} for _, v := range pool { - inPool[v.Instance] = true + draw.inPool[v.Instance] = true } // A binding of an owner naming the feature through its path constrains it as well. for current, path := inst, name; current != nil; { for _, binding := range ctx.bindingsOf(current, path) { - end := bindingEndForPath(binding, path) - if end < 0 { - continue - } - constrains, err := ctx.constrainingBinding(current, binding) - if err != nil { - return nil, nil, err - } - if !constrains { - continue - } - other, err := ctx.resolveBindingEndpoint(current, binding, 1-end) - if err != nil { + if err := ctx.drawBindingWitnesses(draw, inst, current, binding, path); err != nil { return nil, nil, err } - // An end drawing its own values meanwhile has none to share yet; nor, for a binding an - // owner declares, one that will draw them from what it subsets when read. - if slices.ContainsFunc(other.locations, func(loc bindingLocation) bool { - return ctx.drawingSubsetters[featureValueRef{instance: loc.instance.ID, feature: loc.name}] || - (current != inst && ctx.drawsFromSubsetted(loc)) - }) { - continue - } - for _, loc := range other.locations { - if src, ok := loc.instance.FeatureValues[loc.name]; ok && src != fv { - ctx.listRead(src, fv) - } - } - // An end that cannot be read shares nothing here; validating the binding reports why. - val, found, err := ctx.bindingEndpointValue(other, current, true) - if err != nil || !found { - continue - } - candidates, err := ctx.HeldElements(val) - if err != nil { - continue - } - admits := func(v Value) bool { - return v.Kind == ValInstance && (!pooled || inPool[v.Instance]) && !seen[v.Instance] - } - links, _ := ctx.bindingLinks(binding) - for n := int64(0); n < links; n++ { - pick := slices.IndexFunc(candidates, func(v Value) bool { return admits(v) && !taken[v.Instance] }) - if pick < 0 { - pick = slices.IndexFunc(candidates, admits) - } - if pick < 0 { - break - } - chosen, seen = appendUniqueInstances(chosen, candidates[pick:pick+1], seen) - } } owner, ownerFeature := current.Owner() if owner == nil || ownerFeature == "" { @@ -1180,7 +1130,77 @@ func (ctx *Context) bindingWitnesses(inst *Instance, name string, pool []Value, path = ownerFeature + "." + path current = owner } - return chosen, seen, nil + return draw.chosen, draw.seen, nil +} + +// witnessDraw is one bindingWitnesses draw: the feature drawn for, the pool it may draw +// from, what sibling subsetters hold, and what it has chosen so far. +type witnessDraw struct { + fv *FeatureValue + pooled bool + inPool map[int64]bool + taken map[int64]bool + chosen []Value + seen map[int64]bool +} + +// drawBindingWitnesses draws from the other end of binding, when it constrains path at +// current, as many of that end's values as the binding links. +func (ctx *Context) drawBindingWitnesses(draw *witnessDraw, inst, current *Instance, binding lower.Binding, path string) error { + end := bindingEndForPath(binding, path) + if end < 0 { + return nil + } + constrains, err := ctx.constrainingBinding(current, binding) + if err != nil || !constrains { + return err + } + other, err := ctx.resolveBindingEndpoint(current, binding, 1-end) + if err != nil { + return err + } + // An end drawing its own values meanwhile has none to share yet; nor, for a binding an + // owner declares, one that will draw them from what it subsets when read. + if slices.ContainsFunc(other.locations, func(loc bindingLocation) bool { + return ctx.drawingSubsetters[featureValueRef{instance: loc.instance.ID, feature: loc.name}] || + (current != inst && ctx.drawsFromSubsetted(loc)) + }) { + return nil + } + for _, loc := range other.locations { + if src, ok := loc.instance.FeatureValues[loc.name]; ok && src != draw.fv { + ctx.listRead(src, draw.fv) + } + } + // An end that cannot be read shares nothing here; validating the binding reports why. + val, found, err := ctx.bindingEndpointValue(other, current, true) + if err != nil || !found { + return nil + } + candidates, err := ctx.HeldElements(val) + if err != nil { + return nil + } + links, _ := ctx.bindingLinks(binding) + draw.pick(candidates, links) + return nil +} + +// pick draws up to links admitted candidates, those no sibling subsetter holds first. +func (d *witnessDraw) pick(candidates []Value, links int64) { + admits := func(v Value) bool { + return v.Kind == ValInstance && (!d.pooled || d.inPool[v.Instance]) && !d.seen[v.Instance] + } + for n := int64(0); n < links; n++ { + pick := slices.IndexFunc(candidates, func(v Value) bool { return admits(v) && !d.taken[v.Instance] }) + if pick < 0 { + pick = slices.IndexFunc(candidates, admits) + } + if pick < 0 { + return + } + d.chosen, d.seen = appendUniqueInstances(d.chosen, candidates[pick:pick+1], d.seen) + } } // drawsFromSubsetted reports a feature at loc that holds no value yet and states none of its From 9ed7ce5e3ab6bac7b6c3eadfc4dd699ac4e50102 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 03:14:24 +0000 Subject: [PATCH 2/2] refactor(runtime): shorten the binding-site comment Co-Authored-By: jason.han --- internal/exec/runtime/invoke_calc.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/internal/exec/runtime/invoke_calc.go b/internal/exec/runtime/invoke_calc.go index 45d36d0d94..a28054694c 100644 --- a/internal/exec/runtime/invoke_calc.go +++ b/internal/exec/runtime/invoke_calc.go @@ -923,11 +923,8 @@ func (ctx *Context) leaveCalc() { ctx.calcDepth-- } -// calcBindingSite is where an invocation's parameters are bound beyond the call -// itself: nested, when non-null, is the environment reading a nested usage, which -// the bindings the usage itself declares are written in and evaluate against; -// occurrence is the performance the bound values are also written to; open names -// the parameters a usage read as a feature leaves unsupplied, bound to no value. +// calcBindingSite is where a usage's bindings land beyond the call: the nested +// reader they evaluate in, the performance written to, and the open parameters. type calcBindingSite struct { nested *EvalContext occurrence *calcOccurrence