Skip to content

Issue #448 : a caught error closes only the upvalues of the frames it discards - #542

Open
eldologuzzo wants to merge 1 commit into
yuin:masterfrom
eldologuzzo:fix-pcall-upvalues
Open

eldologuzzo wants to merge 1 commit into
yuin:masterfrom
eldologuzzo:fix-pcall-upvalues

Conversation

@eldologuzzo

Copy link
Copy Markdown

Fixes #448 .

Changes proposed in this pull request:

  • raiseError and Error called closeAllUpvalues() on every error. That closes the open upvalues of all frames up to the root, including the frames that called pcall and keep running after it returns. From then on a closure and the frame that owns the variable no longer share it (the example in bug: pcall affecting function upvalues #448; also a test harness whose pass/fail counters stop being updated after the first caught error).
  • With xpcall nothing was closed, so a closure created inside the failed call kept pointing at a stack register that the next call reused.
  • New LState.pcallBase: the register base of the innermost PCall, set for the duration of the call and restored afterwards. An error closes the upvalues from that base. With no pcall active the base is 0, so an unprotected error still closes everything, as before.
  • PCall closes the same range when it recovers an error, which also covers a Go panic and an xpcall handler (both skip the close in raiseError).
  • Edited _state.go; state.go is regenerated with make build.
  • Tests:
    • _glua-tests/issues.lua: the bug: pcall affecting function upvalues #448 example plus several cases (Go function errors, runtime errors, a caller two frames up, nested pcall, closures created inside failed pcall/xpcall/coroutines). On current master the first one fails with attempt to call a non-function object; with this change they all pass and match Lua 5.1.
    • TestUnprotectedErrorClosesUpvalues keeps the previous behavior for unprotected errors.
    • go test passes.

🤖 Generated with Claude Code

…s it discards

raiseError and Error called closeAllUpvalues() on every error, closing the
open upvalues of all frames up to the root, including the frames that called
pcall and keep running. A closure and the frame that owns the variable then
stop sharing it. With xpcall nothing was closed, so a closure created inside
the failed call read a register that the next call reused.

LState.pcallBase records the register base of the innermost PCall. An error
closes the upvalues from that base (from 0 when no pcall is active, as
before), and PCall closes the same range when it recovers, also after a Go
panic or an xpcall handler.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 7, 2026 21:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Nested protected calls do not restore hasErrorFunc, allowing premature upvalue closure before an outer xpcall handler runs.

1 open finding
What changed in this PR

Fixes upvalue handling when protected Lua calls discard stack frames.

Changes:

  • Tracks the innermost protected-call register base.
  • Closes only upvalues owned by discarded frames.
  • Adds regression coverage for protected and unprotected errors.
File Description
value.go Adds protected-call state tracking.
_state.go Implements scoped upvalue closing.
state.go Regenerates the runtime implementation.
state_test.go Tests unprotected-error behavior.
_glua-tests/​issues.lua Adds issue #448 regression scenarios.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread _state.go
Comment on lines +1825 to +1828
// This call is the innermost protected boundary until it returns (#448).
oldPcallBase := ls.pcallBase
ls.pcallBase = base
defer func() { ls.pcallBase = oldPcallBase }()
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.

bug: pcall affecting function upvalues

2 participants