Skip to content

fix(compiler): expand multi-values in obj:method(...) calls#248

Merged
davydog187 merged 1 commit into
mainfrom
fix-method-call-multi-value-expansion
May 27, 2026
Merged

fix(compiler): expand multi-values in obj:method(...) calls#248
davydog187 merged 1 commit into
mainfrom
fix-method-call-multi-value-expansion

Conversation

@davydog187
Copy link
Copy Markdown
Contributor

Summary

  • Fixes Method-call obj:method(unpack(args)) drops all but first unpacked value #247: obj:m(unpack(vals)) was dropping every value after the first because the method-call codegen hardcoded a fixed arg_count.
  • gen_expr(%Expr.MethodCall{...}) now mirrors the last-argument classification (:vararg / :multi_call / :normal) that Expr.Call already had, offset by one register for the implicit self slot.
  • No executor change needed — :call dispatch operates purely on register layout regardless of whether the function reached R[base] via :self or :move.

The user-visible knock-on from #247("%s,%s,%s"):format(table.unpack(args)) raising a MatchError in string.format — is a symptom of the same bug and is resolved by this change. It surfaced in real code through say/init.lua (str:format(unpack(strings))), which is what luassert uses.

Test plan

  • mix test test/language/function_test.exs — 5 new regressions covering table.unpack, vararg, inner-call expansion, leading fixed args before an unpack, and the string:format knock-on
  • mix test — full suite passes (1889 tests, 0 failures, 30 skipped)
  • Both reproductions from Method-call obj:method(unpack(args)) drops all but first unpacked value #247 produce the documented expected output (n=3, [a,b,c])

The method-call codegen hardcoded `arg_count = length(args) + 1`, so
`obj:m(unpack(vals))` compiled as a call with a fixed argument count and
discarded every unpacked value after the first. Reference Lua treats
`t:m(unpack(vals))` as sugar for `t.m(t, vals[1], vals[2], ...)`.

Extend `gen_expr(%Expr.MethodCall{...})` to use the same last-argument
classification (`:vararg` / `:multi_call` / `:normal`) that `Expr.Call`
already has, offset by one register for the implicit `self` slot:

- vararg tail   → `call(base, -(arg_count + 2), ...)`
- inner call    → patch inner result_count to -2; outer
  `call(base, {:multi, 1 + fixed_count}, ...)`
- normal        → unchanged

No executor change needed: `:call` dispatch operates purely on register
layout regardless of whether the function reached `R[base]` via `:self`
or `:move`.

Fixes #247.
@davydog187 davydog187 merged commit 695f4a1 into main May 27, 2026
5 checks passed
@davydog187 davydog187 deleted the fix-method-call-multi-value-expansion branch May 27, 2026 19:24
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.

Method-call obj:method(unpack(args)) drops all but first unpacked value

1 participant