#setArgsFromStack currently handles constant call arguments by forwarding the raw operandConstant(...) term into #setLocalValue:
rule <k> #setArgFromStack(IDX, operandConstant(_) #as CONSTOPERAND)
=>
#setLocalValue(place(local(IDX), .ProjectionElems), CONSTOPERAND)
...
</k>
This is unsafe if the #setLocalValue write rule can fire before the constant operand is evaluated/decoded to a Value. In that case, the callee local may be written as typedValue(operandConstant(...), TY, MUT).
That violates the local-storage invariant documented in rt/value.md and rt/data.md: locals are TypedLocals, and a TypedValue must carry a Value payload. Later rules that rely on getValue(...), isValue(...), or projection traversal may then get stuck or continue with an invalid local payload.
The fix should keep the scope limited to call-argument setup:
- add a regression test for passing a constant argument into a callee and reading it there;
- ensure the constant argument is evaluated/decoded to a
Value before it is stored in callee <locals>;
- prevent raw
Operand terms such as operandConstant(...) from appearing inside typedValue(...);
- preserve the existing
operandCopy / operandMove handling for caller locals, reference-height adjustment, and moved locals.
It is fine for an unevaluated construct to enter locals only through an explicit thunk(...), since that is itself a Value; the issue is storing a raw Operand as the TypedValue payload.
#setArgsFromStackcurrently handles constant call arguments by forwarding the rawoperandConstant(...)term into#setLocalValue:This is unsafe if the
#setLocalValuewrite rule can fire before the constant operand is evaluated/decoded to aValue. In that case, the callee local may be written astypedValue(operandConstant(...), TY, MUT).That violates the local-storage invariant documented in
rt/value.mdandrt/data.md: locals areTypedLocals, and aTypedValuemust carry aValuepayload. Later rules that rely ongetValue(...),isValue(...), or projection traversal may then get stuck or continue with an invalid local payload.The fix should keep the scope limited to call-argument setup:
Valuebefore it is stored in callee<locals>;Operandterms such asoperandConstant(...)from appearing insidetypedValue(...);operandCopy/operandMovehandling for caller locals, reference-height adjustment, and moved locals.It is fine for an unevaluated construct to enter locals only through an explicit
thunk(...), since that is itself aValue; the issue is storing a rawOperandas theTypedValuepayload.