Skip to content

VM: OpAssignment in-place dispatch needs runtime fallback like the interpreter #101

@timfennis

Description

@timfennis

Background

The VM compiler now uses the in-place version of compound-assignment operators (e.g. |=, &=, ++=) when resolved_assign_operation is Binding::Resolved. This is necessary to preserve reference semantics — when let y = x; x |= rhs, both x and y should see the updated map because they share an underlying Rc.

The problem

The interpreter handles OpAssignment by trying both the in-place op and the regular op at runtime, falling back on FunctionTypeMismatch:

let mut operations_to_try = [
    (resolved_assign_operation, true),   // in-place, try first
    (resolved_operation, false),         // regular, fallback
];
while let Some((op, in_place)) = operations_to_try.next() {
    match func.call_checked(...) {
        Err(FunctionTypeMismatch) => continue,   // try next
        ...
    }
}

The VM has no equivalent fallback. The current workaround (Binding::Resolved check) avoids the worst cases (e.g. -= on integers where only the map's -= exists, returned as Binding::Dynamic from the broad fallback in resolve_function_binding), but it breaks down in cases where:

  • The in-place op is Binding::Dynamic because the static type is Any at compile time, but the value is actually a map/list at runtime and should use in-place
  • More complex overload resolution scenarios

Desired fix

The VM compiler should emit bytecode that attempts the in-place call and, on type mismatch, falls back to the regular call + SetVar. This likely requires either:

  1. A new opcode (e.g. CallInPlaceWithFallback) that encapsulates the try/fallback logic
  2. Or a runtime flag/sentinel mechanism that allows conditional jumps after a "soft" call attempt

Related code

  • ndc_vm/src/compiler.rsExpression::OpAssignment match arm
  • ndc_interpreter/src/evaluate/mod.rsExpression::OpAssignment match arm (reference implementation)
  • ndc_interpreter/src/semantic/scope.rsresolve_function_binding (the broad fallback in the third .or_else)

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions