fix(vm): pass raw error values through pcall and add §6.1 position prefix#335
Merged
Conversation
Red matrix over both engines: pcall/xpcall must return the raised Lua value as-is (table, number, boolean, nil), error() must prefix string messages with source:line: (level 0 suppresses), and internal TypeError/ArgumentError messages must stay strings. Plan: A48
…efix
error(value) now raises the value verbatim and pcall/xpcall hand it back
to Lua untouched (table, number, boolean, nil) instead of stringifying.
String messages gain the source:line: prefix per Lua 5.3 §6.1, honoring
level 0 suppression; the prefixed view rides a new Lua-facing :lua_value
field on RuntimeError so host rendering keeps reading the raw :value and
never doubles the location. Under the dispatcher, native calls inside
compiled closures publish a {nil, source} position so the prefix is
omitted rather than attributed to a stale line.
Plan: A48
Closes #334
davydog187
commented
Jun 4, 2026
| # sites that read `current_position/0` (e.g. `error()`'s §6.1 position | ||
| # prefix) then omit the line rather than attribute a wrong one. Restored | ||
| # after the call so nested invocations don't leak. | ||
| prev_pos = Process.get(@position_key, @unset) |
Contributor
Author
There was a problem hiding this comment.
Process dictionary is really a hack. I think this was from a previous design choice made for performance that we need to revisit
Contributor
Author
There was a problem hiding this comment.
we will evaluate this later
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
pcall/xpcall pass the raw Lua error value through; error() adds the §6.1 position prefix
Plan:
.agents/plans/A48-pcall-error-value-passthrough.mdCloses #334
Goal
Per Lua 5.3 §6.1,
error(value)raises an arbitrary Lua value andpcallreturns it as-is as its second result;xpcallhands it to the message handler untouched. Previouslyextract_error_message/1stringified every non-string value (error({code = 1})→"table: 0x..."). Additionally, string messages now gain the referencesource:line:position prefix (error("boom")→"file:1: boom"), suppressed bylevel == 0— all without disturbing host-facing error rendering.How
lua_errorcaptures thelevelargument and builds the prefixed view into a new Lua-facing-only:lua_valuefield onRuntimeError;:valuestays raw, somessage/to_map/stringify(and the(error object is a TYPE value)host formatting) are provably untouched — no doubled location.extract_error_message/1→error_value/1:lua_value→ rawvalue(matched on key presence, soerror(nil)/error(false)pass through) →Exception.messagefallback forArgumentError/plain Elixir exceptions.{nil, source}position, so the prefix is suppressed rather than attributed to a stale outer line (the dispatcher strips per-instruction lines at encode time). The interpreter attributes correctly.Success criteria
test/lua/vm/pcall_error_value_test.exs(26 cases, both engines): table/number/boolean/false/nilpassthrough, prefix shape + source-first ordering, level-0 suppression, xpcall raw handler value, erroring-handler fallback to the original raw value, TypeError/bad-argument regression guards — all green (red-first, commit 424c65a)mix test: 2234 passed, 0 failures; host-message canaries unchanged (error_gallery_test.exs,integration_test.exs:1382,lua_test.exs:940,runtime_exception_test.exs:34-35)mix test --only lua53: 17 passed / 12 skipped — identical to main baselinemix format+mix compile --warnings-as-errorsclean (mix credois not a task in this repo — see Discoveries)Changes
Discoveries
assert, bad-argument) inside nested compiled closures now render a source-only host header (at <eval>:) where they previously had no location header at all ({nil, nil}position). Verified via before/after canary: strictly added information, no wrong line, no test pinned the old bytes;ErrorFormatter.format_location(source, nil)is an explicit supported clause.mix credo, but credo is not a dependency of this repo.error()inside compiled closures gets a real line (collapsing the engine-conditional prefix assertions) anderror(msg, 2)caller attribution becomes implementable.Out of scope (intentional)
error()level >= 2 (needs per-frame line numbers; treated as level 1)TypeError/ArgumentError— hostErrorFormatteralready renders location; many tests pin exact strings)test/lua53_tests/errors.lua(blocked on an unrelatedload()parse-error-prefix mismatch; its level-0 anderror()-nil cases are reproduced in the new unit test)Verification