Skip to content

Commit b07518e

Browse files
matzclaude
andcommitted
parse.y: implement &nil in formal parameters
`&nil` is recently introduced in CRuby to explicitly declare that a method does not accept a block. When a block is passed, ArgumentError "no block accepted" is raised. This is analogous to `**nil` for keyword arguments. The noblock flag is encoded in bit 23 of OP_ENTER's aspec operand (24=n1:m5:o5:r1:m5:k5:d1:b1), avoiding the need for a new opcode. Co-authored-by: Claude <noreply@anthropic.com>
1 parent 337cf4b commit b07518e

8 files changed

Lines changed: 2780 additions & 2800 deletions

File tree

include/mruby.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,11 @@ MRB_API struct RClass* mrb_define_module_under_id(mrb_state *mrb, struct RClass
922922
*/
923923
#define MRB_ARGS_BLOCK() ((mrb_aspec)1)
924924

925+
/**
926+
* Function does not accept a block (&nil)
927+
*/
928+
#define MRB_ARGS_NOBLOCK() ((mrb_aspec)(1 << 23))
929+
925930
/**
926931
* Function accepts any number of arguments
927932
*/

include/mruby/ops.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ OPCODE(CALL, Z) /* self.call(*, **, &) (But overlay the current cal
6969
OPCODE(BLKCALL, BB) /* R[a] = R[a].call(R[a+1],... ,R[a+b]); direct block call */
7070
OPCODE(SUPER, BB) /* R[a] = super(R[a+1],... ,R[a+b+1]) */
7171
OPCODE(ARGARY, BS) /* R[a] = argument array (16=m5:r1:m5:d1:lv4) */
72-
OPCODE(ENTER, W) /* arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1) */
72+
OPCODE(ENTER, W) /* arg setup according to flags (24=n1:m5:o5:r1:m5:k5:d1:b1) */
7373
OPCODE(KEY_P, BB) /* R[a] = kdict.key?(Syms[b]) */
7474
OPCODE(KEYEND, Z) /* raise unless kdict.empty? */
7575
OPCODE(KARG, BB) /* R[a] = kdict[Syms[b]]; kdict.delete(Syms[b]) */

include/mruby/proc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ struct RProc {
7272
#define MRB_ASPEC_KEY(a) (((a) >> 2) & 0x1f)
7373
#define MRB_ASPEC_KDICT(a) (((a) >> 1) & 0x1)
7474
#define MRB_ASPEC_BLOCK(a) ((a) & 1)
75+
#define MRB_ASPEC_NOBLOCK(a) (((a) >> 23) & 0x1)
7576

7677
#define MRB_PROC_CFUNC_FL 128
7778
#define MRB_PROC_CFUNC_P(p) (((p)->flags & MRB_PROC_CFUNC_FL) != 0)

mrbgems/mruby-compiler/core/codegen.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2401,13 +2401,16 @@ lambda_body(codegen_scope *s, node *locals, struct mrb_ast_args *args, node *bod
24012401
/* keyword arguments */
24022402
ka = args->keyword_args ? node_len(args->keyword_args) : 0;
24032403
kd = args->kwrest_arg ? 1 : 0;
2404-
ba = args->block_arg ? 1 : 0;
2404+
/* &nil: no block accepted (noblock flag in aspec) */
2405+
mrb_bool noblock = args->block_arg == MRB_SYM(nil);
2406+
ba = (args->block_arg && !noblock) ? 1 : 0;
24052407

24062408
if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) {
24072409
codegen_error(s, "too many formal arguments");
24082410
}
2409-
/* (23bits = 5:5:1:5:5:1:1) */
2410-
a = MRB_ARGS_REQ(ma)
2411+
/* (24bits = 1:5:5:1:5:5:1:1) */
2412+
a = (noblock ? MRB_ARGS_NOBLOCK() : 0)
2413+
| MRB_ARGS_REQ(ma)
24112414
| MRB_ARGS_OPT(oa)
24122415
| (ra ? MRB_ARGS_REST() : 0)
24132416
| MRB_ARGS_POST(pa)

mrbgems/mruby-compiler/core/parse.y

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1215,7 +1215,7 @@ new_args_tail(parser_state *p, node *kws, mrb_sym kwrest, mrb_sym blk)
12151215
}
12161216

12171217
local_add_blk(p);
1218-
if (blk) local_add_f(p, blk);
1218+
if (blk && blk != MRB_SYM(nil)) local_add_f(p, blk);
12191219

12201220
/* allocate register for keywords arguments */
12211221
/* order is for Proc#parameters */
@@ -4861,6 +4861,10 @@ f_block_arg : blkarg_mark tIDENTIFIER
48614861
{
48624862
$$ = $2;
48634863
}
4864+
| blkarg_mark keyword_nil
4865+
{
4866+
$$ = MRB_SYM(nil);
4867+
}
48644868
| blkarg_mark
48654869
{
48664870
$$ = intern_op(and);
@@ -8047,6 +8051,8 @@ dump_args(mrb_state *mrb, struct mrb_ast_args *args, int offset, uint16_t lineno
80478051
dump_prefix(offset, lineno);
80488052
if (blk == MRB_OPSYM(and))
80498053
printf("blk=&\n");
8054+
else if (blk == MRB_SYM(nil))
8055+
printf("blk=&nil\n");
80508056
else
80518057
printf("blk=&%s\n", mrb_sym_name(mrb, blk));
80528058
}

mrbgems/mruby-compiler/core/y.tab.c

Lines changed: 2751 additions & 2793 deletions
Large diffs are not rendered by default.

src/codedump.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,14 +374,15 @@ codedump(mrb_state *mrb, const mrb_irep *irep, FILE *out)
374374
print_lv_a(mrb, irep, a, out);
375375
break;
376376
CASE(OP_ENTER, W):
377-
fprintf(out, "ENTER\t\t%d:%d:%d:%d:%d:%d:%d (0x%x)\n",
377+
fprintf(out, "ENTER\t\t%d:%d:%d:%d:%d:%d:%d:%d (0x%x)\n",
378378
MRB_ASPEC_REQ(a),
379379
MRB_ASPEC_OPT(a),
380380
MRB_ASPEC_REST(a),
381381
MRB_ASPEC_POST(a),
382382
MRB_ASPEC_KEY(a),
383383
MRB_ASPEC_KDICT(a),
384-
MRB_ASPEC_BLOCK(a), a);
384+
MRB_ASPEC_BLOCK(a),
385+
MRB_ASPEC_NOBLOCK(a), a);
385386
break;
386387
CASE(OP_KEY_P, BB):
387388
fprintf(out, "KEY_P\t\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b]));

src/vm.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2558,6 +2558,12 @@ mrb_vm_exec(mrb_state *mrb, const struct RProc *begin_proc, const mrb_code *iseq
25582558

25592559
mrb_value * const argv0 = argv;
25602560
mrb_value blk = regs[ci_bidx(ci)];
2561+
2562+
/* &nil: reject block */
2563+
if (MRB_ASPEC_NOBLOCK(a) && !mrb_nil_p(blk)) {
2564+
RAISE_LIT(mrb, E_ARGUMENT_ERROR, "no block accepted");
2565+
}
2566+
25612567
mrb_value kdict = mrb_nil_value();
25622568

25632569
/* keyword arguments */

0 commit comments

Comments
 (0)