Skip to content

fix(runtime): explore every order of unordered statements in calc, constraint and case bodies - #864

Merged
HuiJun merged 53 commits into
developfrom
fix/atomic-body-statement-order
Oct 4, 2026
Merged

HuiJun merged 53 commits into
developfrom
fix/atomic-body-statement-order

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Depends on #840 and #829 and must merge after both. Both are merged into this branch, so until they land the diff against develop also shows their commits; the changes of this PR are the two commits fix(runtime): explore every order of a calc or constraint body's unordered statements and fix(lower): leave the steps of a case stating no succession unordered plus the merge resolutions, the self-model's scheduler choice-kind count (now thirteen, for the new guard-order choice) and the pilot baseline's re-recorded examples digest.

What and why

A calc body ran its statements in declaration order inside one step, and explore/-engine check reported that single answer as proved. For

calc def Ord { return : Integer;
  attribute y : Integer := 1;
  assign y := y * 10;
  assign y := y + 2;
  y }

the library admits 12 and 30, but explore said r = 12, proved over schedules: 1 linearization, and check said no violation, exhaustive. Constraint bodies (#829), guards evaluating them, and analysis/verification case steps with no succession hid orders the same way.

  • Calc and constraint bodies. Statements no then relates are unordered among themselves. explore, -engine check, replay and seeded schedules reach every order; the body stays atomic (no other performance runs between its statements) and the result expression / condition is evaluated after them. Independence reuses fix(runtime): explore the order of a body's statements no succession orders #840's footprints (lower/statement_order.go CalcBodyStatementOrder, ConstraintBodyStatementOrder), so commuting statements add no choice. Nested if/while/for and action-usage blocks inside calcs are covered. then between two of a constraint body's own statements now orders them instead of being refused.
  • One choice per invocation. A pure calc or constraint whose body reorders, itself or through a callee (runtime/order_analysis.go reordersTransitively), is one statement order choice point between the distinct results its orders produce (runtime/invocation_statement_order.go), memoized per arguments within the outermost such invocation. A recursive order-dependent calc therefore explores its two outcomes in two runs instead of one choice per level.
  • Guards. A decision/transition guard, change trigger, entry guard or terminal condition whose verdict depends on statement order chooses between the distinct verdicts, an evaluation error being its own alternative (runtime/guard_statement_order.go). Inside a nested preview an order-dependent verdict, and a guard whose constraint body may write outside its performance, fail with a typed not-covered error. A check property is violated when any order violates it.
  • Bounds. A local order sweep is capped at 1024 evaluations; hitting it is reported as the statement orders bound by check and as incomplete by explore, never as proved or exhaustive.
  • Compiled calcs. Under the ordering schedules a calc whose body (or callee) reorders is interpreted instead of compiled; run keeps compiled speed.
  • Case steps. An analysis or verification case stating no succession leaves its steps unordered, as an action definition's subactions are, so explore/check interleave them.
  • Fixed schedules. declared and reverse (the default) run calc and constraint bodies and unordered case steps in declaration order, as they do nested action bodies, so default results do not move. (Reversing case steps would have moved an existing Monte Carlo test, so they keep declaration order.)
  • A stated succession in a calc body is refused naming the construct ( `first` statement) instead of a Go type name.

Existing fixtures whose bodies meant an order got an explicit then: calc_iterative_factorial, calc_output_assigned_in_body, calc_block_flow_node_unvalued_pin, calc_collection_ops_in_while_loop, calc_rk4_lunar_descent, constraint_body_steps, constraint_body_steps_order. No existing expectation or trace golden changed.

Not in this PR: -constraint, -requirement and -validate still evaluate once under the default schedule when -schedule explore is given (they label the result observed).

Specification basis

SysML v2 7.19/7.20: a calculation body is an action body, and Actions.sysml makes assignments, ifSubactions and loops subactions, which no succession orders. Cases.sysml makes case steps subactions likewise. Rows moved in docs/project/spec-compliance.md: a new calc/constraint unordered-statement row (⚠️ Approximate: the fixed schedules use declaration order, a tool-defined linearization), the constraint-body row, and the case-steps row (✅ → ⚠️ for the same reason).

How it was verified

  • Conformance: calc_explore_statement_order ({12, 30}; default 12; trace goldens under default, declared, seed-1), _commuting, _declared, _derived, _recursive, _recursive_deep, _then, _then_unordered; constraint_explore_statement_order; action_guard_statement_order; state_transition_guard_statement_order; analysis_explore_step_order (+ traces, _declared), verification_explore_step_order (+ _declared), analysis_case_step_order_commuting, analysis_case_step_order_stated.
  • TestRuntimeRobustnessAtomicBodyOrder (budget exhaustion reports incomplete under explore and a bound under check, construct-named refusal, SMT not covered, deep recursion ends in a typed budget error, impure guard refused, enclosing-binding calls not memoized) and the case-step robustness test.
  • lower unit tests for calc/constraint/case orders; parser golden calc_statement_succession; SMT atomic_body_order_test.go.
  • go build ./..., go vet ./..., gofmt -l . (empty), make lint, make docs-check, python3 scripts/changelog.py check, go test ./internal/exec/... ./internal/ir/... ./internal/check/... ./internal/translate/... ./internal/frontend/repl/... ./tests/..., go test -race ./internal/exec/runtime/.
  • Run counts (TestExecutionConformance$|TestCheckConformance, against develop + fix(runtime): explore the order of a body's statements no succession orders #840 + feat(runtime): execute constraint-body steps and complete model-determined extents #829 without this PR): no existing case's outcome or run count changed across 128 counter-bearing cases; the new fixtures run in 2–3 runs each.

Checklist

  • make test and make lint pass locally (make lint and the package suites above pass; full make test is left to CI)
  • Tests added or updated for the change
  • Documentation extended where it already covers the surface (see CONTRIBUTING.md)
  • Changelog entry added as changes/unreleased/<slug>.<section>.md, not as an edit to CHANGELOG.md
  • baselines regenerated and make docs-counts run if a gate count moved (compliance rows need nothing: the census is counted at docs build)
  • No internal work-item labels (waves, slices, F4, K5) in the body, docs, or changelog

Link to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/27169e4b94084c969cd9f23e601f6d11
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/27169e4b94084c969cd9f23e601f6d11?variant=devin
Requested by: @HuiJun

devin-ai-integration Bot and others added 30 commits October 2, 2026 19:10
…on bodies

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ields

Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ers in extents

A namespace-owned binding connector (lowered via lower.NamespaceBindings)
makes its ends denote the same value(s): two valueless usages share one
object classified by both usages' types, a valueless end bound to a
feature chain denotes the chain's object, valued ends are consistency-
checked with ErrBindingConflict, and non-1..1 multiplicities refuse with
the typed ErrBindingEnd semantics nested bindings use.

The extent walk (only the walk) also reads and collects the values of
action, state, connection, interface, allocation and flow usages —
perform and exhibit included — through extentHeldFeature, while
extentHeldMember keeps library-declared performances from making their
holder a candidate.

A namespace-level collection usage denotes its subsetters' objects first
and anonymous members only to make up its lower bound; an abstract usage
contributes none of its own, and under- or over-counts refuse with a
typed ErrMultiplicityViolation naming the usage.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
… model index

The once-per-model namespace usage index (namespaceModelIndex) walks every
document's namespace scopes a single time, producing each usage's
subsetting usages and the namespace-owned bindings' equivalence classes —
union-find over their resolved end usages — so a binding written in
another package and several bindings on one usage both count.

A class's value is the one a valued member declares or a chain end
evaluates to (several, pairwise equal or BindingConflictError naming the
two ends that differ), or one object materialized for the
earliest-declared member and classified by every member's types,
recorded for each member so the result and the recorded state are
identical whichever member is read first; a chain end reading a member
of its own class is CyclicBindingError, and the multiplicity refusal
stays per binding.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…on bodies

A step typed by an action, or an action a body performs, runs in an executor of its own outside the graph BodyDivides reads, so its start shot and assignment ran as one move and explore/check missed the lost update. Under one-move schedules a callee's start shot is now a boundary, its bodies divide where two moves touch shared state, and its flow pauses after each such move. An if's guard is evaluated before its branch (IfThenPerformance), so a dividing body yields between them. Statement nodes resolve their footprints in their own frame's graph.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
A constraint or requirement body's statements are steps of one Boolean
performance, run in declaration order before its conditions are evaluated
in the frame they left: locals live in a fresh frame per check, parameters
are copied into it, and each condition is judged on its own Required.
Writes reaching outside the performance (ErrConstraintExternalAssignment,
SysML v2 §7.17.9), chained or qualified targets, send, perform and
terminate (ErrConstraintEffect), stated successions
(ErrStatementNotExecutable) and steps-only bodies (ErrNoConditions) are
refused. The solver refuses to translate bodies stating steps.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…t and moves

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…onstraints

The unvalued-declaration marking introduced for constraint bodies changed
calc bodies' behavior (an unvalued 'attribute x;' now answers missing
rather than null, breaking assignments to it and changing result errors);
restrict it to the constraint host so calc/action/state bodies bind null
as before. Adds the order-dependent constraint trace case, a
subject-reading requirement and a specialized-def usage to the
conformance case, and assume/negation subtests to the robustness suite.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…d SysML

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…red when dividing its flow

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…flow

Each write to an output lands at the invoking node's pin and goes on along its streaming flows as it is made, so a performance beside the callee may observe it between two writes. Only the callee's attributes and in parameters are its own.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
… and recorded occurrences, refresh the namespace index on RegisterScope, and let nested bodies read outer step locals

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…ording its value

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…behaviors and take the largest member lower bound

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…n rebuilding it from objects

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…eclared-value seam as from an expression read

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…lueless scalar bindings undetermined

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…denotes

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…orders

Direct body statements with no succession between them are unordered subactions, so explore, the model checker, replay and seeded runs now choose among those that may run next (a statement-order choice point), reducing orders that only swap independent statements; declared and reverse keep declaration order. A terminate action usage's body takes successions, and its implicit terminate is ordered against the body. A loop or if node of a do body's stated flow yields after each iteration and branch statement. Fixtures that meant an order state it with then.

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…h that node

Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
… usage again

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…dy-statements

Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

devin-ai-integration Bot and others added 6 commits October 3, 2026 20:31
…ent-order

Co-Authored-By: jason.han <hanhuijun@gmail.com>

# Conflicts:
#	docs/project/pilot-differential-baseline.json
Co-Authored-By: jason.han <hanhuijun@gmail.com>
Co-Authored-By: jason.han <hanhuijun@gmail.com>
@devin-ai-integration
devin-ai-integration Bot marked this pull request as ready for review October 4, 2026 00:26
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 3 commits October 4, 2026 00:45
…nary reason to its firing

Co-Authored-By: jason.han <hanhuijun@gmail.com>
…atement-order

Co-Authored-By: jason.han <hanhuijun@gmail.com>

# Conflicts:
#	docs/project/pilot-differential-baseline.json
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.

1 participant