Project

General

Profile

Actions

Bug #22310

open

parse.y rejects a variable binding that precedes an unrelated alternative pattern

Bug #22310: parse.y rejects a variable binding that precedes an unrelated alternative pattern

Added by yuki300 (y h) 7 days ago. Updated 6 days ago.

Status:
Open
Assignee:
-
Target version:
-
ruby -v:
ruby 4.0.4 (2026-05-12 revision b89eb1bcbf) +PRISM [arm64-darwin25]
[ruby-core:126676]

Description

parse.y rejects patterns in which a variable binding appears before
an alternative pattern, even when the alternative itself binds nothing.
Prism accepts the same code. Is this intended?

Reproduction

# ok.rb
case [:a, 1]
in [ :a | :b, x ]
end

# ng.rb
case [1, :a]
in [ x, :a | :b ]
end
$ ruby --parser=parse.y -c ok.rb
Syntax OK
$ ruby --parser=parse.y -c ng.rb
ng.rb:2: alternative pattern after variable capture (SyntaxError)

$ ruby --parser=prism -c ng.rb
Syntax OK

The two patterns are semantically identical; only the order of the
array elements differs. Both files run without error under the default
parser.

Other forms rejected by parse.y (all accepted by Prism)

in { a: String => t, b: Integer | String }          # binding, then alternative
in { a: String => t, b: { c: Integer | String } }   # alternative nested deeper
in [ one, "a" | "b" => two ]                        # form reported in rails/bootsnap issue 539
in { access_token: String => token, expires_in: (Integer | String) => expires_in }
                                                    # form shipped in the anthropic gem

Reversing the order makes them pass:

in { b: Integer | String, a: String => t }          # Syntax OK

For contrast, a binding inside the alternation is rejected by both
parsers on 4.0.4, as expected:

in [ x, :a | y ]    # variable capture in alternative pattern (parse.y and Prism)

Why this looks unintended

:a | :b and Integer | String bind no variables, so the documented
restriction -- "Binding to variables currently does NOT work for
alternative patterns joined with |" -- is not violated. Nothing can be
left unbound.

The check in p_alt tests p->ctxt.capture_in_pattern, which is set by
any binding anywhere in the same in clause, not only by a binding on
the left-hand side of the |:

p_alt		: p_alt[left] '|'[alt]
                    {
                        p->ctxt.in_alt_pattern = 1;
                    }
                  p_expr_basic[right]
                    {
                        if (p->ctxt.capture_in_pattern) {
                            yyerror1(&@alt, "alternative pattern after variable capture");
                        }

Prism instead visits the left-hand node when it reaches the |
(parse_pattern_alternation_error in prism.c), so sibling bindings are
not affected.

The check was introduced in
https://github.com/ruby/ruby/commit/f4b6a5191ceb0ed0cd7a3e3c8bab24cc0dd15736
([Feature #21572]). The discussion in #21572 and in the dev meeting
(https://github.com/ruby/dev-meeting-log/blob/master/2025/DevMeeting-2025-10-23.md)
only covers bindings inside an alternation, and
test/ruby/test_pattern_matching.rb only asserts the passing order
(in [ :a | :b, x]). The p_alt rule is unchanged on current master.

Impact

Not visible under the default parser. However,
RubyVM::InstructionSequence.compile_file was routed to parse.y until
[Bug #22023] was fixed, so bootsnap (which uses it to build its bytecode
cache) made gems fail to load on Ruby 4.0.1-4.0.3 while ruby foo.rb
worked. That path is fixed in 4.0.4 and worked around in bootsnap
1.24.2, but the parse.y check itself remains.

Prior sightings

The same rejection has been observed before and was each time attributed
to the compile_file routing bug ([Bug #22023]):

As far as I can find, whether the parse.y check itself is correct has not
been raised.

Environment

ruby 4.0.4 (2026-05-12 revision b89eb1bcbf) +PRISM [arm64-darwin25]   # rejects with --parser=parse.y
ruby 4.0.3 (2026-04-21 revision 85ddef263a) +PRISM [arm64-darwin25]   # same; also via compile_file
ruby 3.4.9 (2026-03-11 revision 76cca827ab) +PRISM [arm64-darwin25]   # parse.y accepts all of the above

Related issues 1 (0 open1 closed)

Related to Ruby - Feature #21572: Make illegal variable in alternation pattern a syntax errorClosedActions
Actions

Also available in: PDF Atom