Skip to content

deps: parser v0.13.1, so END { } runs instead of killing the process - #809

Merged
tannevaled merged 1 commit into
mainfrom
deps/parser-v0.13.1
Oct 10, 2026
Merged

tannevaled merged 1 commit into
mainfrom
deps/parser-v0.13.1

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Closes #805.

What it was

$ echo 'END { puts "E" }' > p.rb && rbgo p.rb
panic: runtime error: slice bounds out of range [:1] with capacity 0
$ echo $?
2

END { } has been in Ruby since 1.0. For a program this is a crash instead of
an answer; for an embedder it is worse — ruby.Run does not return an
error, it takes the host process down, and nothing the host writes around the
call prevents it.

A Go zero value that means something

END { } desugars to at_exit { }, and the Block the parser built left
SplatIndex at Go's zero value. Zero is a perfectly good index, and that
field's "no splat" value is -1:

SplatIndex int // index of the top-level *splat param in Params, or -1

So the block announced a *splat parameter at position 0 with no parameters to
hold it, and this compiler's rest-binding believed it and read past an empty
environment.

One line upstream: go-ruby-parser/parser#52, released as v0.13.1 — a
patch, as gorelease -base v0.13.0 reports, since no exported API moved.

rbgo before rbgo after MRI 4.0.7
END { puts "E" } panic, exit 2 E E
puts "main" + END panic main E main E
BEGIN + main + END panic B main E B main E

Why the regression test lives here too

Not belt and braces — the defect was invisible in the AST. The parser's own
suite was green throughout, because a parser does not run what it builds: the
entire cost landed on the consumer that lowers a Block. A downgrade of this
dependency brings the crash back, and this is what notices.

It asserts the ordering as well as the absence of a crash: END and
at_exit handlers run LIFO, measured on both engines — MRI 4.0.7 and rbgo agree
on second registered then first registered. A test that only checked "did it
crash" would pass on a handler that never ran. BEGIN and at_exit sit in the
same test as controls, so a regression in any of the three is attributable to
the right half.

The README row goes in the same change

The row added this morning says END { } crashes the process. It did, and
this is the commit that stops it — so the row goes now. A document that is wrong
on the day it ships is precisely what #801 was about.

🤖 Generated with Claude Code

`END { }` -- ordinary Ruby, in the language since 1.0 -- ended the process with
an uncontained Go panic when the handler ran at exit:

    $ echo 'END { puts "E" }' > p.rb && rbgo p.rb
    panic: runtime error: slice bounds out of range [:1] with capacity 0
    $ echo $?
    2

For a program that is a crash instead of an answer. For an EMBEDDER it is worse:
ruby.Run does not return an error, it takes the host process down, and nothing
the host writes around the call prevents it. #805.

The cause was upstream and is one line. `END { }` desugars to `at_exit { }`, and
the Block the parser built left SplatIndex at Go's ZERO VALUE -- which is a
perfectly good index, where that field's "no splat" value is -1. So the block
announced a *splat parameter at position 0 with no parameters to hold it, and
this compiler's rest-binding believed it and read past an empty environment.
go-ruby-parser/parser#52, released as v0.13.1 (a patch: no exported API moved).

The regression test lives here as well as upstream, and that is the point rather
than belt and braces: the defect was INVISIBLE IN THE AST. The parser's own
suite was green throughout, because a parser does not run what it builds -- the
entire cost landed on the consumer that lowers a Block. A downgrade of this
dependency brings the crash back, and this is what notices.

It asserts the ORDERING too, not just the absence of a crash: END and at_exit
handlers run LIFO, measured on both engines (MRI 4.0.7 and rbgo agree, "second
registered" then "first registered"). BEGIN and at_exit are in the same test as
controls, so a regression in any of the three is attributable to the right half.

The README row added this morning said END crashes the process. It did, and this
is the commit that stops it, so the row goes in the same change -- a document
that is wrong on the day it ships is the thing yesterday's doc sweep was about.

Closes #805.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit bae7ee9 into main Oct 10, 2026
9 checks passed
@tannevaled
tannevaled deleted the deps/parser-v0.13.1 branch October 10, 2026 19:14
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.

END { } crashes the process with a Go panic: valid Ruby source takes the embedder's host down

1 participant