diff --git a/imemo.c b/imemo.c index 9154f6bc6ac280..3814fbd4538d85 100644 --- a/imemo.c +++ b/imemo.c @@ -645,11 +645,7 @@ rb_imemo_free(VALUE obj) case imemo_callinfo:{ const struct rb_callinfo *ci = ((const struct rb_callinfo *)obj); - if (ci->kwarg) { - if (RUBY_ATOMIC_FETCH_SUB(((struct rb_callinfo_kwarg *)ci->kwarg)->references, 1) == 1) { - ruby_xfree_sized((void *)ci->kwarg, rb_callinfo_kwarg_bytes(ci->kwarg->keyword_len)); - } - } + rb_callinfo_kwarg_release((struct rb_callinfo_kwarg *)ci->kwarg); RB_DEBUG_COUNTER_INC(obj_imemo_callinfo); break; diff --git a/prism/prism.c b/prism/prism.c index e3137d3167e7db..d997c63d166e06 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -15290,6 +15290,12 @@ parse_block(pm_parser_t *parser, uint16_t depth) { statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_BRACES, (uint16_t) (depth + 1))); } + /* Pop before consuming the closing `}` so the following token (e.g. a + * `do`) is lexed in the enclosing context rather than as a block + * belonging to this block's interior. Otherwise a `do` block would + * wrongly bind to a command whose argument ends in a brace block, as in + * `foo(m a { } do end)`. */ + pm_accepts_block_stack_pop(parser); expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE, &opening); } else { if (!match1(parser, PM_TOKEN_KEYWORD_END)) { @@ -15305,6 +15311,8 @@ parse_block(pm_parser_t *parser, uint16_t depth) { } } + /* As with the brace case above, pop before consuming `end`. */ + pm_accepts_block_stack_pop(parser); expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END, &opening); } @@ -15313,7 +15321,6 @@ parse_block(pm_parser_t *parser, uint16_t depth) { pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &opening, &parser->previous); pm_parser_scope_pop(parser); - pm_accepts_block_stack_pop(parser); return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous); } diff --git a/prism_compile.c b/prism_compile.c index 353e018d878493..e70477a51c786e 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -3887,6 +3887,8 @@ pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *c ELEM_INSERT_NEXT(opt_new_prelude, &new_insn_body(iseq, location.line, location.node_id, BIN(putnil), 0)->link); } + rb_callinfo_kwarg_retain(kw_arg); + // Jump unless the receiver uses the "basic" implementation of "new" VALUE ci; if (flags & VM_CALL_FORWARDING) { @@ -3909,6 +3911,8 @@ pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *c PUSH_LABEL(ret, not_basic_new_finish); PUSH_INSN(ret, location, pop); + + rb_callinfo_kwarg_release(kw_arg); } else { PUSH_SEND_R(ret, location, method_id, INT2FIX(orig_argc), block_iseq, INT2FIX(flags), kw_arg); diff --git a/test/prism/errors/do_block_after_command_block.txt b/test/prism/errors/do_block_after_command_block.txt new file mode 100644 index 00000000000000..0989695af0c130 --- /dev/null +++ b/test/prism/errors/do_block_after_command_block.txt @@ -0,0 +1,14 @@ +foo(m a { } do end) + ^~ unexpected 'do'; expected a `)` to close the arguments + ^~ unexpected 'do', expecting end-of-input + ^~ unexpected 'do', ignoring it + ^~~ unexpected 'end', ignoring it + ^ unexpected ')', ignoring it + +foo(m a.b { } do end) + ^~ unexpected 'do'; expected a `)` to close the arguments + ^~ unexpected 'do', expecting end-of-input + ^~ unexpected 'do', ignoring it + ^~~ unexpected 'end', ignoring it + ^ unexpected ')', ignoring it + diff --git a/vm_callinfo.h b/vm_callinfo.h index 9a6c69deaee35d..0ff0d89d1a5617 100644 --- a/vm_callinfo.h +++ b/vm_callinfo.h @@ -61,6 +61,20 @@ rb_callinfo_kwarg_bytes(int keyword_len) rb_eRuntimeError); } +static inline void +rb_callinfo_kwarg_retain(struct rb_callinfo_kwarg *kwarg) +{ + if (kwarg) RUBY_ATOMIC_INC(kwarg->references); +} + +static inline void +rb_callinfo_kwarg_release(struct rb_callinfo_kwarg *kwarg) +{ + if (kwarg && RUBY_ATOMIC_FETCH_SUB(kwarg->references, 1) == 1) { + ruby_xfree_sized(kwarg, rb_callinfo_kwarg_bytes(kwarg->keyword_len)); + } +} + // imemo_callinfo struct rb_callinfo { VALUE flags;