From: "yuki300 (Yuki Hiro) via ruby-core" Date: 2026-09-14T19:13:01+00:00 Subject: [ruby-core:126700] [Ruby Bug#22310] parse.y rejects a variable binding that precedes an unrelated alternative pattern Issue #22310 has been updated by yuki300 (Yuki Hiro). Filed the `**rest` one separately as #22314 (fix in https://github.com/ruby/ruby/pull/18831). ---------------------------------------- Bug #22310: parse.y rejects a variable binding that precedes an unrelated alternative pattern https://bugs.ruby-lang.org/issues/22310#change-118998 * Author: yuki300 (Yuki Hiro) * Status: Open * ruby -v: ruby 4.0.4 (2026-05-12 revision b89eb1bcbf) +PRISM [arm64-darwin25] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- `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 ```ruby # 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) ```ruby 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: ```ruby 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: ```ruby 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 `|`: ```c 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]): - https://github.com/rails/bootsnap/issues/539 (cause identified as https://github.com/ruby/ruby/pull/16463, later backported as #22023) - https://github.com/ruby/irb/issues/1212 ("in Ruby `4.0.2` `parse.y` has a bug, but the important part here is that this is using `parse.y`") - https://github.com/anthropics/anthropic-sdk-ruby/pull/192, whose author noticed that swapping the hash-pattern key order avoids the error 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 ``` -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/