diff options
35 files changed, 612 insertions, 321 deletions
@@ -69,7 +69,7 @@ releases. * minitest 6.0.1 * test-unit 3.7.7 * rss 0.3.2 -* net-imap 0.6.2 +* net-imap 0.6.3 * rbs 3.10.3 * typeprof 0.31.1 * debug 1.11.1 @@ -2096,7 +2096,7 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons if (node_args) { struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq); - struct rb_args_info *args = &RNODE_ARGS(node_args)->nd_ainfo; + const struct rb_args_info *const args = &RNODE_ARGS(node_args)->nd_ainfo; ID rest_id = 0; int last_comma = 0; ID block_id = 0; @@ -2193,7 +2193,10 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons body->param.flags.accepts_no_kwarg = TRUE; } - if (block_id) { + if (args->no_blockarg) { + body->param.flags.accepts_no_block = TRUE; + } + else if (block_id) { body->param.block_start = arg_size++; body->param.flags.has_block = TRUE; iseq_set_use_block(iseq); @@ -13678,7 +13681,8 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq) (body->param.flags.anon_rest << 10) | (body->param.flags.anon_kwrest << 11) | (body->param.flags.use_block << 12) | - (body->param.flags.forwardable << 13) ; + (body->param.flags.forwardable << 13) | + (body->param.flags.accepts_no_block << 14); #if IBF_ISEQ_ENABLE_LOCAL_BUFFER # define IBF_BODY_OFFSET(x) (x) @@ -13898,6 +13902,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) load_body->param.flags.anon_kwrest = (param_flags >> 11) & 1; load_body->param.flags.use_block = (param_flags >> 12) & 1; load_body->param.flags.forwardable = (param_flags >> 13) & 1; + load_body->param.flags.accepts_no_block = (param_flags >> 14) & 1; load_body->param.size = param_size; load_body->param.lead_num = param_lead_num; load_body->param.opt_num = param_opt_num; diff --git a/doc/language/packed_data.rdoc b/doc/language/packed_data.rdoc index 1a6d80bd4e..0c84113643 100644 --- a/doc/language/packed_data.rdoc +++ b/doc/language/packed_data.rdoc @@ -726,4 +726,4 @@ for one byte in the input or output string. - <tt>'^'</tt> - Only for unpacking; the current position: - "foo\0\0\0".unpack("Z*C") # => ["foo", 6] + "foo\0\0\0".unpack("Z*^") # => ["foo", 4] diff --git a/gems/bundled_gems b/gems/bundled_gems index 6d79be7a97..6a0f626de9 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -12,7 +12,7 @@ rake 13.3.1 https://github.com/ruby/rake test-unit 3.7.7 https://github.com/test-unit/test-unit rexml 3.4.4 https://github.com/ruby/rexml rss 0.3.2 https://github.com/ruby/rss -net-imap 0.6.2 https://github.com/ruby/net-imap d9ae35ef913a45f83387b8444cdce4fb1cbf01af +net-imap 0.6.3 https://github.com/ruby/net-imap net-smtp 0.5.1 https://github.com/ruby/net-smtp matrix 0.4.3 https://github.com/ruby/matrix prime 0.1.4 https://github.com/ruby/prime @@ -3787,7 +3787,13 @@ rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc) } rb_ary_push(args, a); } - if (body->param.flags.has_block) { + if (body->param.flags.accepts_no_block) { + ID noblock; + CONST_ID(noblock, "noblock"); + PARAM_TYPE(noblock); + rb_ary_push(args, a); + } + else if (body->param.flags.has_block) { CONST_ID(block, "block"); rb_ary_push(args, PARAM(body->param.block_start, block)); } diff --git a/lib/prism/node_ext.rb b/lib/prism/node_ext.rb index 469e54ca0c..1bb3ebcf5f 100644 --- a/lib/prism/node_ext.rb +++ b/lib/prism/node_ext.rb @@ -316,7 +316,13 @@ module Prism names << [:nokey] end - names << [:block, block.name || :&] if block + case block + when BlockParameterNode + names << [:block, block.name || :&] + when NoBlockParameterNode + names << [:noblock] + end + names end end @@ -6562,6 +6562,11 @@ f_block_arg : blkarg_mark tIDENTIFIER $$ = $2; /*% ripper: blockarg!($:2) %*/ } + | blkarg_mark keyword_nil + { + $$ = idNil; + /*% ripper: blockarg!(ID2VAL(idNil)) %*/ + } | blkarg_mark { arg_var(p, idFWD_BLOCK); @@ -14474,6 +14479,10 @@ new_args_tail(struct parser_params *p, rb_node_kw_arg_t *kw_args, ID kw_rest_arg struct rb_args_info *args = &node->nd_ainfo; if (p->error_p) return node; + if (block == idNil) { + block = 0; + args->no_blockarg = TRUE; + } args->block_arg = block; args->kw_args = kw_args; diff --git a/prism/config.yml b/prism/config.yml index f6e99c66b1..0736f5a0bf 100644 --- a/prism/config.yml +++ b/prism/config.yml @@ -3920,6 +3920,18 @@ nodes: nil ^^^ + - name: NoBlockParameterNode + fields: + - name: operator_loc + type: location + - name: keyword_loc + type: location + comment: | + Represents the use of `&nil` inside method arguments. + + def a(&nil) + ^^^^ + end - name: NoKeywordsParameterNode fields: - name: operator_loc @@ -4066,7 +4078,9 @@ nodes: - NoKeywordsParameterNode - name: block type: node? - kind: BlockParameterNode + kind: + - BlockParameterNode + - NoBlockParameterNode comment: | Represents the list of parameters on a method, block, or lambda definition. diff --git a/prism/prism.c b/prism/prism.c index 34e5d38b0a..81768024f2 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -5561,6 +5561,24 @@ pm_nil_node_create(pm_parser_t *parser, const pm_token_t *token) { /** * Allocate and initialize a new NoKeywordsParameterNode node. */ +static pm_no_block_parameter_node_t * +pm_no_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) { + assert(operator->type == PM_TOKEN_AMPERSAND || operator->type == PM_TOKEN_UAMPERSAND); + assert(keyword->type == PM_TOKEN_KEYWORD_NIL); + pm_no_block_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_no_block_parameter_node_t); + + *node = (pm_no_block_parameter_node_t) { + .base = PM_NODE_INIT(parser, PM_NO_BLOCK_PARAMETER_NODE, 0, PM_LOCATION_INIT_TOKENS(parser, operator, keyword)), + .operator_loc = TOK2LOC(parser, operator), + .keyword_loc = TOK2LOC(parser, keyword) + }; + + return node; +} + +/** + * Allocate and initialize a new NoKeywordsParameterNode node. + */ static pm_no_keywords_parameter_node_t * pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) { assert(operator->type == PM_TOKEN_USTAR_STAR || operator->type == PM_TOKEN_STAR_STAR); @@ -5787,9 +5805,9 @@ pm_parameters_node_keyword_rest_set(pm_parameters_node_t *params, pm_node_t *par * Set the block parameter on a ParametersNode node. */ static void -pm_parameters_node_block_set(pm_parameters_node_t *params, pm_block_parameter_node_t *param) { +pm_parameters_node_block_set(pm_parameters_node_t *params, pm_node_t *param) { assert(params->block == NULL); - pm_parameters_node_location_set(params, UP(param)); + pm_parameters_node_location_set(params, param); params->block = param; } @@ -9783,6 +9801,12 @@ parser_lex(pm_parser_t *parser) { unsigned int semantic_token_seen = parser->semantic_token_seen; parser->semantic_token_seen = true; + // We'll jump to this label when we are about to encounter an EOF. + // If we still have lex_modes on the stack, we pop them so that cleanup + // can happen. For example, we should still continue parsing after a heredoc + // identifier, even if the heredoc body was syntax invalid. + switch_lex_modes: + switch (parser->lex_modes.current->mode) { case PM_LEX_DEFAULT: case PM_LEX_EMBEXPR: @@ -9856,6 +9880,14 @@ parser_lex(pm_parser_t *parser) { // We'll check if we're at the end of the file. If we are, then we // need to return the EOF token. if (parser->current.end >= parser->end) { + // We may be missing closing tokens. We should pop modes one by one + // to do the appropriate cleanup like moving next_start for heredocs. + // Only when no mode is remaining will we actually emit the EOF token. + if (parser->lex_modes.current->mode != PM_LEX_DEFAULT) { + lex_mode_pop(parser); + goto switch_lex_modes; + } + // If we hit EOF, but the EOF came immediately after a newline, // set the start of the token to the newline. This way any EOF // errors will be reported as happening on that line rather than @@ -13915,26 +13947,33 @@ parse_parameters( parser_lex(parser); pm_token_t operator = parser->previous; - pm_token_t name = { 0 }; + pm_node_t *param; - bool repeated = false; - if (accept1(parser, PM_TOKEN_IDENTIFIER)) { - name = parser->previous; - repeated = pm_parser_parameter_name_check(parser, &name); - pm_parser_local_add_token(parser, &name, 1); + if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) { + param = (pm_node_t *) pm_no_block_parameter_node_create(parser, &operator, &parser->previous); } else { - parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK; - } + pm_token_t name = {0}; - pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, NTOK2PTR(name), &operator); - if (repeated) { - pm_node_flag_set_repeated_parameter(UP(param)); + bool repeated = false; + if (accept1(parser, PM_TOKEN_IDENTIFIER)) { + name = parser->previous; + repeated = pm_parser_parameter_name_check(parser, &name); + pm_parser_local_add_token(parser, &name, 1); + } else { + parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK; + } + + param = (pm_node_t *) pm_block_parameter_node_create(parser, NTOK2PTR(name), &operator); + if (repeated) { + pm_node_flag_set_repeated_parameter(param); + } } + if (params->block == NULL) { pm_parameters_node_block_set(params, param); } else { - pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_BLOCK_MULTI); - pm_parameters_node_posts_append(params, UP(param)); + pm_parser_err_node(parser, param, PM_ERR_PARAMETER_BLOCK_MULTI); + pm_parameters_node_posts_append(params, param); } break; @@ -15433,7 +15472,7 @@ parse_string_part(pm_parser_t *parser, uint16_t depth) { pm_token_t opening = parser->previous; pm_statements_node_t *statements = NULL; - if (!match1(parser, PM_TOKEN_EMBEXPR_END)) { + if (!match3(parser, PM_TOKEN_EMBEXPR_END, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) { pm_accepts_block_stack_push(parser, true); statements = parse_statements(parser, PM_CONTEXT_EMBEXPR, (uint16_t) (depth + 1)); pm_accepts_block_stack_pop(parser); diff --git a/prism_compile.c b/prism_compile.c index 5d83e55a77..c8b80e21ca 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -4524,6 +4524,7 @@ pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, const pm_node_l case PM_PARAMETERS_NODE: case PM_KEYWORD_REST_PARAMETER_NODE: case PM_NO_KEYWORDS_PARAMETER_NODE: + case PM_NO_BLOCK_PARAMETER_NODE: case PM_NUMBERED_PARAMETERS_NODE: case PM_OPTIONAL_KEYWORD_PARAMETER_NODE: case PM_OPTIONAL_PARAMETER_NODE: @@ -6340,7 +6341,7 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod } } - if (parameters_node && parameters_node->block) { + if (parameters_node && parameters_node->block && PM_NODE_TYPE_P(parameters_node->block, PM_BLOCK_PARAMETER_NODE)) { const pm_block_parameter_node_t *block_node = (const pm_block_parameter_node_t *) parameters_node->block; if (PM_NODE_FLAG_P(block_node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER) || !block_node->name) { @@ -6686,26 +6687,38 @@ pm_compile_scope_node(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_nod // def foo(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m, &n) // ^^ if (parameters_node->block) { - body->param.block_start = local_index; - body->param.flags.has_block = true; - iseq_set_use_block(iseq); + switch (PM_NODE_TYPE(parameters_node->block)) { + case PM_BLOCK_PARAMETER_NODE: { + body->param.block_start = local_index; + body->param.flags.has_block = true; - pm_constant_id_t name = ((const pm_block_parameter_node_t *) parameters_node->block)->name; + iseq_set_use_block(iseq); - if (name) { - if (PM_NODE_FLAG_P(parameters_node->block, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { - ID local = pm_constant_id_lookup(scope_node, name); - local_table_for_iseq->ids[local_index] = local; + pm_constant_id_t name = ((const pm_block_parameter_node_t *) parameters_node->block)->name; + + if (name) { + if (PM_NODE_FLAG_P(parameters_node->block, PM_PARAMETER_FLAGS_REPEATED_PARAMETER)) { + ID local = pm_constant_id_lookup(scope_node, name); + local_table_for_iseq->ids[local_index] = local; + } + else { + pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node); + } } else { - pm_insert_local_index(name, local_index, index_lookup_table, local_table_for_iseq, scope_node); + pm_insert_local_special(idAnd, local_index, index_lookup_table, local_table_for_iseq); } - } - else { - pm_insert_local_special(idAnd, local_index, index_lookup_table, local_table_for_iseq); - } - local_index++; + local_index++; + break; + } + case PM_NO_BLOCK_PARAMETER_NODE: { + body->param.flags.accepts_no_block = true; + break; + } + default: + rb_bug("node type %s not expected as block parameter", pm_node_type_to_str(PM_NODE_TYPE(parameters_node->block))); + } } } @@ -9955,6 +9968,12 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, return; } + case PM_NO_BLOCK_PARAMETER_NODE: { + // def foo(&nil); end + // ^^^^ + ISEQ_BODY(iseq)->param.flags.accepts_no_block = TRUE; + return; + } case PM_NO_KEYWORDS_PARAMETER_NODE: { // def foo(**nil); end // ^^^^^ @@ -3433,6 +3433,7 @@ method_inspect(VALUE method) const VALUE keyrest = ID2SYM(rb_intern("keyrest")); const VALUE block = ID2SYM(rb_intern("block")); const VALUE nokey = ID2SYM(rb_intern("nokey")); + const VALUE noblock = ID2SYM(rb_intern("noblock")); int forwarding = 0; rb_str_buf_cat2(str, "("); @@ -3467,6 +3468,9 @@ method_inspect(VALUE method) else if (kind == nokey) { name = rb_str_new2("nil"); } + else if (kind == noblock) { + name = rb_str_new2("nil"); + } else { name = Qnil; } @@ -3519,6 +3523,9 @@ method_inspect(VALUE method) else if (kind == nokey) { rb_str_buf_cat2(str, "**nil"); } + else if (kind == noblock) { + rb_str_buf_cat2(str, "&nil"); + } if (i < RARRAY_LEN(params) - 1) { rb_str_buf_cat2(str, ", "); diff --git a/rubyparser.h b/rubyparser.h index 36a2dc30a6..2ed93e9894 100644 --- a/rubyparser.h +++ b/rubyparser.h @@ -764,6 +764,7 @@ struct rb_args_info { struct RNode_OPT_ARG *opt_args; unsigned int no_kwarg: 1; + unsigned int no_blockarg: 1; unsigned int forwarding: 1; }; diff --git a/spec/ruby/core/method/parameters_spec.rb b/spec/ruby/core/method/parameters_spec.rb index f1c2523cf0..41b9cd8d12 100644 --- a/spec/ruby/core/method/parameters_spec.rb +++ b/spec/ruby/core/method/parameters_spec.rb @@ -22,6 +22,12 @@ describe "Method#parameters" do local_is_not_parameter = {} end + ruby_version_is "4.1" do + eval <<-RUBY + def one_noblock(&nil); end + RUBY + end + def forward_parameters(...) end def underscore_parameters(_, _, _ = 1, *_, _:, _: 2, **_, &_); end @@ -187,6 +193,13 @@ describe "Method#parameters" do m.parameters.should == [[:nokey]] end + ruby_version_is "4.1" do + it "returns [[:noblock]] for a method with a single &nil parameter" do + m = MethodSpecs::Methods.instance_method(:one_noblock) + m.parameters.should == [[:noblock]] + end + end + it "works with ->(){} as the value of an optional argument" do m = MethodSpecs::Methods.instance_method(:one_opt_with_stabby) m.parameters.should == [[:opt,:a]] diff --git a/spec/ruby/core/proc/parameters_spec.rb b/spec/ruby/core/proc/parameters_spec.rb index cf8a8f5b12..0876ad0daf 100644 --- a/spec/ruby/core/proc/parameters_spec.rb +++ b/spec/ruby/core/proc/parameters_spec.rb @@ -172,4 +172,12 @@ describe "Proc#parameters" do eval("lambda { it }").parameters.should == [[:req]] end end + + ruby_version_is "4.1" do + it "returns :noblock for &nil parameter" do + eval <<~RUBY + proc { |&nil| }.parameters.should == [[:noblock]] + RUBY + end + end end diff --git a/spec/ruby/language/block_spec.rb b/spec/ruby/language/block_spec.rb index 67aad76c57..ad6f7190b4 100644 --- a/spec/ruby/language/block_spec.rb +++ b/spec/ruby/language/block_spec.rb @@ -1110,6 +1110,17 @@ describe "`it` calls without arguments in a block" do end end end + + ruby_version_is "4.1" do + it "works alongside disallowed block argument" do + no_block = eval <<-EOF + proc {|arg1, &nil| arg1} + EOF + + no_block.call(:a).should == :a + -> { no_block.call(:a) {} }.should raise_error(ArgumentError, 'no block accepted') + end + end end # Duplicates specs in language/it_parameter_spec.rb diff --git a/spec/ruby/language/method_spec.rb b/spec/ruby/language/method_spec.rb index 8f9f094fd8..dd93703d9f 100644 --- a/spec/ruby/language/method_spec.rb +++ b/spec/ruby/language/method_spec.rb @@ -1127,6 +1127,18 @@ describe "A method" do result = m(1, {foo: :bar}) result.should == [1, nil, nil, {foo: :bar}, nil, {}] end + + ruby_version_is "4.1" do + evaluate <<-ruby do + def m(a, &nil); a end; + ruby + + m(1).should == 1 + + -> { m(1) {} }.should raise_error(ArgumentError, 'no block accepted') + -> { m(1, &proc {}) }.should raise_error(ArgumentError, 'no block accepted') + end + end end context 'when passing an empty keyword splat to a method that does not accept keywords' do diff --git a/test/prism/errors/unterminated_heredoc_and_embexpr.txt b/test/prism/errors/unterminated_heredoc_and_embexpr.txt new file mode 100644 index 0000000000..bed7fcd24e --- /dev/null +++ b/test/prism/errors/unterminated_heredoc_and_embexpr.txt @@ -0,0 +1,11 @@ +<<A+B + ^ unterminated heredoc; can't find string "A" anywhere before EOF + ^ unexpected '+', ignoring it + ^ unterminated heredoc; can't find string "A" anywhere before EOF +#{C + ^ unexpected heredoc ending; expected an argument + ^ unexpected heredoc ending, expecting end-of-input + ^ unexpected heredoc ending, ignoring it + ^ unexpected end-of-input, assuming it is closing the parent top level context +^ expected a `}` to close the embedded expression + diff --git a/test/prism/errors/unterminated_heredoc_and_embexpr_2.txt b/test/prism/errors/unterminated_heredoc_and_embexpr_2.txt new file mode 100644 index 0000000000..a03ff1d212 --- /dev/null +++ b/test/prism/errors/unterminated_heredoc_and_embexpr_2.txt @@ -0,0 +1,9 @@ +<<A+B + ^ unterminated heredoc; can't find string "A" anywhere before EOF +#{C + "#{"} + ^ unterminated string meets end of file + ^ unexpected end-of-input, assuming it is closing the parent top level context + ^ expected a `}` to close the embedded expression + ^ unterminated string; expected a closing delimiter for the interpolated string + ^ expected a `}` to close the embedded expression + diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index b30a0f304d..27610e89d3 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -87,6 +87,28 @@ module Prism assert_nil(statement.end_keyword) end + def test_unclosed_interpolation + statement = Prism.parse_statement("\"\#{") + assert_equal('"', statement.opening) + assert_nil(statement.closing) + + assert_equal(1, statement.parts.count) + assert_equal('#{', statement.parts[0].opening) + assert_equal("", statement.parts[0].closing) + assert_nil(statement.parts[0].statements) + end + + def test_unclosed_heredoc_and_interpolation + statement = Prism.parse_statement("<<D\n\#{") + assert_equal("<<D", statement.opening) + assert_nil(statement.closing) + + assert_equal(1, statement.parts.count) + assert_equal('#{', statement.parts[0].opening) + assert_equal("", statement.parts[0].closing) + assert_nil(statement.parts[0].statements) + end + private def assert_errors(filepath, version) diff --git a/test/prism/lex_test.rb b/test/prism/lex_test.rb index 9a9f203c28..8ea7ce7e9b 100644 --- a/test/prism/lex_test.rb +++ b/test/prism/lex_test.rb @@ -48,11 +48,58 @@ module Prism end if RUBY_VERSION >= "3.3" - def test_lex_compare - prism = Prism.lex_compat(File.read(__FILE__), version: "current").value - ripper = Ripper.lex(File.read(__FILE__)) + def test_lex_compat + source = "foo bar" + prism = Prism.lex_compat(source, version: "current").value + ripper = Ripper.lex(source) assert_equal(ripper, prism) end end + + def test_lex_interpolation_unterminated + assert_equal( + %i[STRING_BEGIN EMBEXPR_BEGIN EOF], + token_types('"#{') + ) + + assert_equal( + %i[STRING_BEGIN EMBEXPR_BEGIN IGNORED_NEWLINE EOF], + token_types('"#{' + "\n") + ) + end + + def test_lex_interpolation_unterminated_with_content + # FIXME: Emits EOL twice. + assert_equal( + %i[STRING_BEGIN EMBEXPR_BEGIN CONSTANT EOF EOF], + token_types('"#{C') + ) + + assert_equal( + %i[STRING_BEGIN EMBEXPR_BEGIN CONSTANT NEWLINE EOF], + token_types('"#{C' + "\n") + ) + end + + def test_lex_heredoc_unterminated + code = <<~'RUBY'.strip + <<A+B + #{C + RUBY + + assert_equal( + %i[HEREDOC_START EMBEXPR_BEGIN CONSTANT HEREDOC_END PLUS CONSTANT NEWLINE EOF], + token_types(code) + ) + + assert_equal( + %i[HEREDOC_START EMBEXPR_BEGIN CONSTANT NEWLINE HEREDOC_END PLUS CONSTANT NEWLINE EOF], + token_types(code + "\n") + ) + end + + def token_types(code) + Prism.lex(code).value.map { |token, _state| token.type } + end end end diff --git a/test/prism/result/source_location_test.rb b/test/prism/result/source_location_test.rb index 993150f581..dbda848211 100644 --- a/test/prism/result/source_location_test.rb +++ b/test/prism/result/source_location_test.rb @@ -650,6 +650,10 @@ module Prism assert_location(NilNode, "nil") end + def test_NoBlockParameterNode + assert_location(NoBlockParameterNode, "def foo(&nil); end", 8...12) { |node| node.parameters.block } + end + def test_NoKeywordsParameterNode assert_location(NoKeywordsParameterNode, "def foo(**nil); end", 8...13) { |node| node.parameters.keyword_rest } end diff --git a/test/prism/ruby/parameters_signature_test.rb b/test/prism/ruby/parameters_signature_test.rb index ea1eea106b..8ca4c565ac 100644 --- a/test/prism/ruby/parameters_signature_test.rb +++ b/test/prism/ruby/parameters_signature_test.rb @@ -50,6 +50,10 @@ module Prism assert_parameters([[:nokey]], "**nil") end + def test_noblock + assert_parameters([[:noblock]], "&nil") + end + def test_keyrest_anonymous assert_parameters([[:keyrest, :**]], "**") end diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb index 6846f7958b..43b1f0f620 100644 --- a/test/ruby/test_iseq.rb +++ b/test/ruby/test_iseq.rb @@ -682,6 +682,17 @@ class TestISeq < Test::Unit::TestCase assert_equal([[:nokey]], iseq.eval.singleton_method(:foo).parameters) end + def test_to_binary_dumps_noblock + iseq = assert_iseq_to_binary(<<-RUBY) + o = Object.new + class << o + def foo(&nil); end + end + o + RUBY + assert_equal([[:noblock]], iseq.eval.singleton_method(:foo).parameters) + end + def test_to_binary_line_info assert_iseq_to_binary("#{<<~"begin;"}\n#{<<~'end;'}", '[Bug #14660]').eval begin; diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index c3819cdebf..7c3e8e03a7 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -32,6 +32,7 @@ class TestMethod < Test::Unit::TestCase def mk7(a, b = nil, *c, d, **o) nil && o end def mk8(a, b = nil, *c, d, e:, f: nil, **o) nil && o end def mnk(**nil) end + def mnb(&nil) end def mf(...) end class Base @@ -617,6 +618,7 @@ class TestMethod < Test::Unit::TestCase define_method(:pmk7) {|a, b = nil, *c, d, **o|} define_method(:pmk8) {|a, b = nil, *c, d, e:, f: nil, **o|} define_method(:pmnk) {|**nil|} + define_method(:pmnb) {|&nil|} def test_bound_parameters assert_equal([], method(:m0).parameters) @@ -640,6 +642,7 @@ class TestMethod < Test::Unit::TestCase assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], method(:mk7).parameters) assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], method(:mk8).parameters) assert_equal([[:nokey]], method(:mnk).parameters) + assert_equal([[:noblock]], method(:mnb).parameters) # pending assert_equal([[:rest, :*], [:keyrest, :**], [:block, :&]], method(:mf).parameters) end @@ -666,6 +669,7 @@ class TestMethod < Test::Unit::TestCase assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], self.class.instance_method(:mk7).parameters) assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], self.class.instance_method(:mk8).parameters) assert_equal([[:nokey]], self.class.instance_method(:mnk).parameters) + assert_equal([[:noblock]], self.class.instance_method(:mnb).parameters) # pending assert_equal([[:rest, :*], [:keyrest, :**], [:block, :&]], self.class.instance_method(:mf).parameters) end @@ -691,6 +695,7 @@ class TestMethod < Test::Unit::TestCase assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], method(:pmk7).parameters) assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], method(:pmk8).parameters) assert_equal([[:nokey]], method(:pmnk).parameters) + assert_equal([[:noblock]], method(:pmnb).parameters) end def test_bmethod_unbound_parameters @@ -715,6 +720,7 @@ class TestMethod < Test::Unit::TestCase assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], self.class.instance_method(:pmk7).parameters) assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], self.class.instance_method(:pmk8).parameters) assert_equal([[:nokey]], self.class.instance_method(:pmnk).parameters) + assert_equal([[:noblock]], self.class.instance_method(:pmnb).parameters) end def test_hidden_parameters diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index e868967e8b..70e1956816 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -202,6 +202,49 @@ class TestSyntax < Test::Unit::TestCase assert_syntax_error("def f(...); g(&); end", /no anonymous block parameter/) end + def test_no_block_argument_in_method + assert_valid_syntax("def f(&nil) end") + assert_valid_syntax("def f(a, &nil) end") + assert_valid_syntax("def f(*rest, &nil) end") + assert_valid_syntax("def f(*rest, p, &nil) end") + assert_valid_syntax("def f(a, *rest, &nil) end") + assert_valid_syntax("def f(a, *rest, p, &nil) end") + assert_valid_syntax("def f(a, k: nil, &nil) end") + assert_valid_syntax("def f(a, k: nil, **kw, &nil) end") + assert_valid_syntax("def f(a, *rest, k: nil, &nil) end") + assert_valid_syntax("def f(a, *rest, k: nil, **kw, &nil) end") + assert_valid_syntax("def f(a, *rest, p, k: nil, &nil) end") + assert_valid_syntax("def f(a, *rest, p, k: nil, **kw, &nil) end") + + obj = Object.new + obj.instance_eval "def f(&nil) end" + assert_raise_with_message(ArgumentError, /block accepted/) {obj.f {}} + assert_raise_with_message(ArgumentError, /block accepted/) {obj.f(&proc {})} + end + + def test_no_block_argument_in_block + assert_valid_syntax("proc do |&nil| end") + assert_valid_syntax("proc do |a, &nil| end") + assert_valid_syntax("proc do |*rest, &nil| end") + assert_valid_syntax("proc do |*rest, p, &nil| end") + assert_valid_syntax("proc do |a, *rest, &nil| end") + assert_valid_syntax("proc do |a, *rest, p, &nil| end") + assert_valid_syntax("proc do |a, k: nil, &nil| end") + assert_valid_syntax("proc do |a, k: nil, **kw, &nil| end") + assert_valid_syntax("proc do |a, *rest, k: nil, &nil| end") + assert_valid_syntax("proc do |a, *rest, k: nil, **kw, &nil| end") + assert_valid_syntax("proc do |a, *rest, p, k: nil, &nil| end") + assert_valid_syntax("proc do |a, *rest, p, k: nil, **kw, &nil| end") + + pr = eval "proc {|&nil|}" + assert_nil(pr.call) + assert_raise_with_message(ArgumentError, /block accepted/) {pr.call {}} + pr = eval "proc {|a, &nil| a}" + assert_nil(pr.call) + assert_equal(1, pr.call(1)) + assert_raise_with_message(ArgumentError, /block accepted/) {pr.call {}} + end + def test_newline_in_block_parameters bug = '[ruby-dev:45292]' ["", "a", "a, b"].product(["", ";x", [";", "x"]]) do |params| @@ -12,6 +12,8 @@ NORETURN(static void raise_argument_error(rb_execution_context_t *ec, const rb_i NORETURN(static void argument_arity_error(rb_execution_context_t *ec, const rb_iseq_t *iseq, const rb_callable_method_entry_t *cme, const int miss_argc, const int min_argc, const int max_argc)); NORETURN(static void argument_kw_error(rb_execution_context_t *ec, const rb_iseq_t *iseq, const rb_callable_method_entry_t *cme, const char *error, const VALUE keys)); VALUE rb_keyword_error_new(const char *error, VALUE keys); /* class.c */ +static VALUE set_error_backtrace(rb_execution_context_t *ec, const rb_iseq_t *iseq, const rb_callable_method_entry_t *cme, const VALUE exc); + static VALUE method_missing(rb_execution_context_t *ec, VALUE obj, ID id, int argc, const VALUE *argv, enum method_missing_reason call_status, int kw_splat); const rb_callable_method_entry_t *rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me); @@ -959,7 +961,15 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co argument_kw_error(ec, iseq, cme, "unknown", rb_hash_keys(keyword_hash)); } - if (ISEQ_BODY(iseq)->param.flags.has_block) { + if (ISEQ_BODY(iseq)->param.flags.accepts_no_block) { + VALUE given_block; + args_setup_block_parameter(ec, calling, &given_block); + if (!NIL_P(given_block)) { + VALUE exc = rb_exc_new_cstr(rb_eArgError, "no block accepted"); + rb_exc_raise(set_error_backtrace(ec, iseq, cme, exc)); + } + } + else if (ISEQ_BODY(iseq)->param.flags.has_block) { if (ISEQ_BODY(iseq)->local_iseq == iseq) { /* Do nothing */ } @@ -981,8 +991,8 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co return opt_pc; } -static void -raise_argument_error(rb_execution_context_t *ec, const rb_iseq_t *iseq, const rb_callable_method_entry_t *cme, const VALUE exc) +static VALUE +set_error_backtrace(rb_execution_context_t *ec, const rb_iseq_t *iseq, const rb_callable_method_entry_t *cme, const VALUE exc) { VALUE at; @@ -1000,6 +1010,13 @@ raise_argument_error(rb_execution_context_t *ec, const rb_iseq_t *iseq, const rb rb_ivar_set(exc, idBt_locations, at); rb_exc_set_backtrace(exc, at); + return exc; +} + +static void +raise_argument_error(rb_execution_context_t *ec, const rb_iseq_t *iseq, const rb_callable_method_entry_t *cme, const VALUE exc) +{ + set_error_backtrace(ec, iseq, cme, exc); rb_exc_raise(exc); } @@ -456,6 +456,7 @@ struct rb_iseq_constant_body { unsigned int anon_kwrest: 1; unsigned int use_block: 1; unsigned int forwardable: 1; + unsigned int accepts_no_block: 1; } flags; unsigned int size; diff --git a/vm_insnhelper.c b/vm_insnhelper.c index a27bf5f49b..95422caaa6 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -2732,7 +2732,8 @@ rb_simple_iseq_p(const rb_iseq_t *iseq) ISEQ_BODY(iseq)->param.flags.has_kwrest == FALSE && ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg == FALSE && ISEQ_BODY(iseq)->param.flags.forwardable == FALSE && - ISEQ_BODY(iseq)->param.flags.has_block == FALSE; + ISEQ_BODY(iseq)->param.flags.has_block == FALSE && + ISEQ_BODY(iseq)->param.flags.accepts_no_block == FALSE; } bool @@ -2745,7 +2746,8 @@ rb_iseq_only_optparam_p(const rb_iseq_t *iseq) ISEQ_BODY(iseq)->param.flags.has_kwrest == FALSE && ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg == FALSE && ISEQ_BODY(iseq)->param.flags.forwardable == FALSE && - ISEQ_BODY(iseq)->param.flags.has_block == FALSE; + ISEQ_BODY(iseq)->param.flags.has_block == FALSE && + ISEQ_BODY(iseq)->param.flags.accepts_no_block == FALSE; } bool @@ -2757,7 +2759,8 @@ rb_iseq_only_kwparam_p(const rb_iseq_t *iseq) ISEQ_BODY(iseq)->param.flags.has_kw == TRUE && ISEQ_BODY(iseq)->param.flags.has_kwrest == FALSE && ISEQ_BODY(iseq)->param.flags.forwardable == FALSE && - ISEQ_BODY(iseq)->param.flags.has_block == FALSE; + ISEQ_BODY(iseq)->param.flags.has_block == FALSE && + ISEQ_BODY(iseq)->param.flags.accepts_no_block == FALSE; } #define ALLOW_HEAP_ARGV (-2) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index c11610e26c..845f7e6eef 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -482,10 +482,10 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::ToRegexp { opt, values, state } => gen_toregexp(jit, asm, *opt, opnds!(values), &function.frame_state(*state)), Insn::Param => unreachable!("block.insns should not have Insn::Param"), Insn::Snapshot { .. } => return Ok(()), // we don't need to do anything for this instruction at the moment - &Insn::Send { cd, blockiseq, state, reason, .. } => gen_send(jit, asm, cd, blockiseq, &function.frame_state(state), reason), + &Insn::Send { cd, blockiseq: None, state, reason, .. } => gen_send_without_block(jit, asm, cd, &function.frame_state(state), reason), + &Insn::Send { cd, blockiseq: Some(blockiseq), state, reason, .. } => gen_send(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::SendForward { cd, blockiseq, state, reason, .. } => gen_send_forward(jit, asm, cd, blockiseq, &function.frame_state(state), reason), Insn::SendDirect { cme, iseq, recv, args, kw_bits, blockiseq, state, .. } => gen_send_iseq_direct(cb, jit, asm, *cme, *iseq, opnd!(recv), opnds!(args), *kw_bits, &function.frame_state(*state), *blockiseq), - &Insn::SendWithoutBlock { cd, state, reason, .. } => gen_send_without_block(jit, asm, cd, &function.frame_state(state), reason), &Insn::InvokeSuper { cd, blockiseq, state, reason, .. } => gen_invokesuper(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::InvokeSuperForward { cd, blockiseq, state, reason, .. } => gen_invokesuperforward(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::InvokeBlock { cd, state, reason, .. } => gen_invokeblock(jit, asm, cd, &function.frame_state(state), reason), diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index a47d9bf61f..0eb47062c9 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -1197,6 +1197,15 @@ pub mod test_utils { }) } + /// Evaluate a given Ruby program with compile options + pub fn eval_with_options(program: &str, options_expr: &str) -> VALUE { + with_rubyvm(|| { + let options = eval(options_expr); + let wrapped_iseq = compile_to_wrapped_iseq_with_options(&unindent(program, false), options); + unsafe { rb_funcallv(wrapped_iseq, ID!(eval), 0, null()) } + }) + } + /// Get the #inspect of a given Ruby program in Rust string pub fn inspect(program: &str) -> String { let inspect = format!("({program}).inspect"); @@ -1252,10 +1261,15 @@ pub mod test_utils { /// Compile a program into a RubyVM::InstructionSequence object fn compile_to_wrapped_iseq(program: &str) -> VALUE { + compile_to_wrapped_iseq_with_options(program, Qnil) + } + + fn compile_to_wrapped_iseq_with_options(program: &str, options: VALUE) -> VALUE { let bytes = program.as_bytes().as_ptr() as *const c_char; unsafe { let program_str = rb_utf8_str_new(bytes, program.len().try_into().unwrap()); - rb_funcallv(rb_cISeq, ID!(compile), 1, &program_str) + let args = [program_str, Qnil, Qnil, VALUE(1_usize.wrapping_shl(1) | 1), options]; + rb_funcallv(rb_cISeq, ID!(compile), args.len() as c_int, args.as_ptr()) } } diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index f178e76728..e1e162c0c2 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -1079,6 +1079,39 @@ impl rb_iseq_constant_body_rb_iseq_parameters__bindgen_ty_1 { } } #[inline] + pub fn accepts_no_block(&self) -> ::std::os::raw::c_uint { + unsafe { ::std::mem::transmute(self._bitfield_1.get(14usize, 1u8) as u32) } + } + #[inline] + pub fn set_accepts_no_block(&mut self, val: ::std::os::raw::c_uint) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + self._bitfield_1.set(14usize, 1u8, val as u64) + } + } + #[inline] + pub unsafe fn accepts_no_block_raw(this: *const Self) -> ::std::os::raw::c_uint { + unsafe { + ::std::mem::transmute(<__BindgenBitfieldUnit<[u8; 2usize]>>::raw_get( + ::std::ptr::addr_of!((*this)._bitfield_1), + 14usize, + 1u8, + ) as u32) + } + } + #[inline] + pub unsafe fn set_accepts_no_block_raw(this: *mut Self, val: ::std::os::raw::c_uint) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + <__BindgenBitfieldUnit<[u8; 2usize]>>::raw_set( + ::std::ptr::addr_of_mut!((*this)._bitfield_1), + 14usize, + 1u8, + val as u64, + ) + } + } + #[inline] pub fn new_bitfield_1( has_lead: ::std::os::raw::c_uint, has_opt: ::std::os::raw::c_uint, @@ -1094,6 +1127,7 @@ impl rb_iseq_constant_body_rb_iseq_parameters__bindgen_ty_1 { anon_kwrest: ::std::os::raw::c_uint, use_block: ::std::os::raw::c_uint, forwardable: ::std::os::raw::c_uint, + accepts_no_block: ::std::os::raw::c_uint, ) -> __BindgenBitfieldUnit<[u8; 2usize]> { let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 2usize]> = Default::default(); __bindgen_bitfield_unit.set(0usize, 1u8, { @@ -1152,6 +1186,10 @@ impl rb_iseq_constant_body_rb_iseq_parameters__bindgen_ty_1 { let forwardable: u32 = unsafe { ::std::mem::transmute(forwardable) }; forwardable as u64 }); + __bindgen_bitfield_unit.set(14usize, 1u8, { + let accepts_no_block: u32 = unsafe { ::std::mem::transmute(accepts_no_block) }; + accepts_no_block as u64 + }); __bindgen_bitfield_unit } } diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 56ff3ca1aa..f44c33845c 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -322,7 +322,7 @@ fn inline_thread_current(fun: &mut hir::Function, block: hir::BlockId, _recv: hi fn inline_kernel_itself(_fun: &mut hir::Function, _block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], _state: hir::InsnId) -> Option<hir::InsnId> { if args.is_empty() { - // No need to coerce the receiver; that is done by the SendWithoutBlock rewriting. + // No need to coerce the receiver; that is done by the Send rewriting. return Some(recv); } None diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 579c327d44..1888d00a98 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -884,7 +884,7 @@ pub enum Insn { /// Call a C function that pushes a frame CCallWithFrame { - cd: *const rb_call_data, // cd for falling back to SendWithoutBlock + cd: *const rb_call_data, // cd for falling back to Send cfunc: *const u8, recv: InsnId, args: Vec<InsnId>, @@ -912,17 +912,10 @@ pub enum Insn { /// Un-optimized fallback implementation (dynamic dispatch) for send-ish instructions /// Ignoring keyword arguments etc for now - SendWithoutBlock { - recv: InsnId, - cd: *const rb_call_data, - args: Vec<InsnId>, - state: InsnId, - reason: SendFallbackReason, - }, Send { recv: InsnId, cd: *const rb_call_data, - blockiseq: IseqPtr, + blockiseq: Option<IseqPtr>, args: Vec<InsnId>, state: InsnId, reason: SendFallbackReason, @@ -1012,7 +1005,7 @@ pub enum Insn { FixnumLShift { left: InsnId, right: InsnId, state: InsnId }, FixnumRShift { left: InsnId, right: InsnId }, - // Distinct from `SendWithoutBlock` with `mid:to_s` because does not have a patch point for String to_s being redefined + // Distinct from `Send` with `mid:to_s` because does not have a patch point for String to_s being redefined ObjToString { val: InsnId, cd: *const rb_call_data, state: InsnId }, AnyToString { val: InsnId, str: InsnId, state: InsnId }, @@ -1187,7 +1180,6 @@ impl Insn { } }, Insn::CCallVariadic { .. } => effects::Any, - Insn::SendWithoutBlock { .. } => effects::Any, Insn::Send { .. } => effects::Any, Insn::SendForward { .. } => effects::Any, Insn::InvokeSuper { .. } => effects::Any, @@ -1449,14 +1441,6 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::Jump(target) => { write!(f, "Jump {target}") } Insn::IfTrue { val, target } => { write!(f, "IfTrue {val}, {target}") } Insn::IfFalse { val, target } => { write!(f, "IfFalse {val}, {target}") } - Insn::SendWithoutBlock { recv, cd, args, reason, .. } => { - write!(f, "SendWithoutBlock {recv}, :{}", ruby_call_method_name(*cd))?; - for arg in args { - write!(f, ", {arg}")?; - } - write!(f, " # SendFallbackReason: {reason}")?; - Ok(()) - } Insn::SendDirect { recv, cd, iseq, args, blockiseq, .. } => { write!(f, "SendDirect {recv}, {:p}, :{} ({:?})", self.ptr_map.map_ptr(blockiseq), ruby_call_method_name(*cd), self.ptr_map.map_ptr(iseq))?; for arg in args { @@ -1468,7 +1452,11 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { // For tests, we want to check HIR snippets textually. Addresses change // between runs, making tests fail. Instead, pick an arbitrary hex value to // use as a "pointer" so we can check the rest of the HIR. - write!(f, "Send {recv}, {:p}, :{}", self.ptr_map.map_ptr(blockiseq), ruby_call_method_name(*cd))?; + if let Some(blockiseq) = *blockiseq { + write!(f, "Send {recv}, {:p}, :{}", self.ptr_map.map_ptr(blockiseq), ruby_call_method_name(*cd))?; + } else { + write!(f, "Send {recv}, :{}", ruby_call_method_name(*cd))?; + } for arg in args { write!(f, ", {arg}")?; } @@ -2275,13 +2263,6 @@ impl Function { str: find!(str), state, }, - &SendWithoutBlock { recv, cd, ref args, state, reason } => SendWithoutBlock { - recv: find!(recv), - cd, - args: find_vec!(args), - state, - reason, - }, &SendDirect { recv, cd, cme, iseq, ref args, kw_bits, blockiseq, state } => SendDirect { recv: find!(recv), cd, @@ -2402,7 +2383,6 @@ impl Function { match self.insns.get_mut(insn_id.0).unwrap() { Send { reason, .. } | SendForward { reason, .. } - | SendWithoutBlock { reason, .. } | InvokeSuper { reason, .. } | InvokeSuperForward { reason, .. } | InvokeBlock { reason, .. } @@ -2522,7 +2502,6 @@ impl Function { Insn::FixnumLShift { .. } => types::Fixnum, Insn::FixnumRShift { .. } => types::Fixnum, Insn::PutSpecialObject { .. } => types::BasicObject, - Insn::SendWithoutBlock { .. } => types::BasicObject, Insn::SendDirect { .. } => types::BasicObject, Insn::Send { .. } => types::BasicObject, Insn::SendForward { .. } => types::BasicObject, @@ -3034,7 +3013,7 @@ impl Function { self.push_insn(block, Insn::GuardNoBitsSet { val: flags, mask: Const::CUInt64(RUBY_ELTS_SHARED as u64), mask_name: Some(ID!(RUBY_ELTS_SHARED)), reason: SideExitReason::GuardNotShared, state }); } - /// Rewrite eligible Send/SendWithoutBlock opcodes into SendDirect + /// Rewrite eligible Send opcodes into SendDirect /// opcodes if we know the target ISEQ statically. This removes run-time method lookups and /// opens the door for inlining. /// Also try and inline constant caches, specialize object allocations, and more. @@ -3045,11 +3024,12 @@ impl Function { assert!(self.blocks[block.0].insns.is_empty()); for insn_id in old_insns { match self.find(insn_id) { - Insn::SendWithoutBlock { recv, args, state, cd, .. } if ruby_call_method_id(cd) == ID!(freeze) && args.is_empty() => + Insn::Send { recv, blockiseq: None, args, state, cd, .. } if ruby_call_method_id(cd) == ID!(freeze) && args.is_empty() => self.try_rewrite_freeze(block, insn_id, recv, state), - Insn::SendWithoutBlock { recv, args, state, cd, .. } if ruby_call_method_id(cd) == ID!(minusat) && args.is_empty() => + Insn::Send { recv, blockiseq: None, args, state, cd, .. } if ruby_call_method_id(cd) == ID!(minusat) && args.is_empty() => self.try_rewrite_uminus(block, insn_id, recv, state), - Insn::SendWithoutBlock { mut recv, cd, args, state, .. } => { + Insn::Send { mut recv, cd, state, blockiseq, args, .. } => { + let has_block = blockiseq.is_some(); let frame_state = self.frame_state(state); let (klass, profiled_type) = match self.resolve_receiver_type(recv, self.type_of(recv), frame_state.insn_idx) { ReceiverTypeResolution::StaticallyKnown { class } => (class, None), @@ -3058,21 +3038,24 @@ impl Function { ReceiverTypeResolution::SkewedMegamorphic { .. } | ReceiverTypeResolution::Megamorphic => { if get_option!(stats) { - self.set_dynamic_send_reason(insn_id, SendWithoutBlockMegamorphic); + let reason = if has_block { SendMegamorphic } else { SendWithoutBlockMegamorphic }; + self.set_dynamic_send_reason(insn_id, reason); } self.push_insn_id(block, insn_id); continue; } ReceiverTypeResolution::Polymorphic => { if get_option!(stats) { - self.set_dynamic_send_reason(insn_id, SendWithoutBlockPolymorphic); + let reason = if has_block { SendPolymorphic } else { SendWithoutBlockPolymorphic }; + self.set_dynamic_send_reason(insn_id, reason); } self.push_insn_id(block, insn_id); continue; } ReceiverTypeResolution::NoProfile => { if get_option!(stats) { - self.set_dynamic_send_reason(insn_id, SendWithoutBlockNoProfiles); + let reason = if has_block { SendNoProfiles } else { SendWithoutBlockNoProfiles }; + self.set_dynamic_send_reason(insn_id, reason); } self.push_insn_id(block, insn_id); continue; @@ -3086,7 +3069,8 @@ impl Function { // Do method lookup let mut cme = unsafe { rb_callable_method_entry(klass, mid) }; if cme.is_null() { - self.set_dynamic_send_reason(insn_id, SendWithoutBlockNotOptimizedMethodType(MethodType::Null)); + let reason = if has_block { SendNotOptimizedMethodType(MethodType::Null) } else { SendWithoutBlockNotOptimizedMethodType(MethodType::Null) }; + self.set_dynamic_send_reason(insn_id, reason); self.push_insn_id(block, insn_id); continue; } // Load an overloaded cme if applicable. See vm_search_cc(). @@ -3098,7 +3082,8 @@ impl Function { (METHOD_VISI_PRIVATE, true) => {} (METHOD_VISI_PROTECTED, true) => {} _ => { - self.set_dynamic_send_reason(insn_id, SendWithoutBlockNotOptimizedNeedPermission); + let reason = if has_block { SendNotOptimizedNeedPermission } else { SendWithoutBlockNotOptimizedNeedPermission }; + self.set_dynamic_send_reason(insn_id, reason); self.push_insn_id(block, insn_id); continue; } } @@ -3121,7 +3106,7 @@ impl Function { // Only specialize positional-positional calls // TODO(max): Handle other kinds of parameter passing let iseq = unsafe { get_def_iseq_ptr((*cme).def) }; - if !can_direct_send(self, block, iseq, ci, insn_id, args.as_slice(), None) { + if !can_direct_send(self, block, iseq, ci, insn_id, args.as_slice(), blockiseq) { self.push_insn_id(block, insn_id); continue; } @@ -3144,9 +3129,9 @@ impl Function { self.push_insn_id(block, insn_id); continue; }; - let send_direct = self.push_insn(block, Insn::SendDirect { recv, cd, cme, iseq, args: processed_args, kw_bits, state: send_state, blockiseq: None }); + let send_direct = self.push_insn(block, Insn::SendDirect { recv, cd, cme, iseq, args: processed_args, kw_bits, state: send_state, blockiseq }); self.make_equal_to(insn_id, send_direct); - } else if def_type == VM_METHOD_TYPE_BMETHOD { + } else if !has_block && def_type == VM_METHOD_TYPE_BMETHOD { let procv = unsafe { rb_get_def_bmethod_proc((*cme).def) }; let proc = unsafe { rb_jit_get_proc_ptr(procv) }; let proc_block = unsafe { &(*proc).block }; @@ -3187,7 +3172,7 @@ impl Function { let send_direct = self.push_insn(block, Insn::SendDirect { recv, cd, cme, iseq, args: processed_args, kw_bits, state: send_state, blockiseq: None }); self.make_equal_to(insn_id, send_direct); - } else if def_type == VM_METHOD_TYPE_IVAR && args.is_empty() { + } else if !has_block && def_type == VM_METHOD_TYPE_IVAR && args.is_empty() { // Check if we're accessing ivars of a Class or Module object as they require single-ractor mode. // We omit gen_prepare_non_leaf_call on gen_getivar, so it's unsafe to raise for multi-ractor mode. if self.is_metaclass(klass) && !self.assume_single_ractor_mode(block, state) { @@ -3207,7 +3192,7 @@ impl Function { let getivar = self.push_insn(block, Insn::GetIvar { self_val: recv, id, ic: std::ptr::null(), state }); self.make_equal_to(insn_id, getivar); - } else if let (VM_METHOD_TYPE_ATTRSET, &[val]) = (def_type, args.as_slice()) { + } else if let (false, VM_METHOD_TYPE_ATTRSET, &[val]) = (has_block, def_type, args.as_slice()) { // Check if we're accessing ivars of a Class or Module object as they require single-ractor mode. // We omit gen_prepare_non_leaf_call on gen_getivar, so it's unsafe to raise for multi-ractor mode. if self.is_metaclass(klass) && !self.assume_single_ractor_mode(block, state) { @@ -3222,7 +3207,7 @@ impl Function { self.push_insn(block, Insn::SetIvar { self_val: recv, id, ic: std::ptr::null(), val, state }); self.make_equal_to(insn_id, val); - } else if def_type == VM_METHOD_TYPE_OPTIMIZED { + } else if !has_block && def_type == VM_METHOD_TYPE_OPTIMIZED { let opt_type: OptimizedMethodType = unsafe { get_cme_def_body_optimized_type(cme) }.into(); match (opt_type, args.as_slice()) { (OptimizedMethodType::Call, _) => { @@ -3309,115 +3294,8 @@ impl Function { }, }; } else { - self.set_dynamic_send_reason(insn_id, SendWithoutBlockNotOptimizedMethodType(MethodType::from(def_type))); - self.push_insn_id(block, insn_id); continue; - } - } - Insn::Send { mut recv, cd, state, blockiseq, args, .. } => { - let frame_state = self.frame_state(state); - let (klass, profiled_type) = match self.resolve_receiver_type(recv, self.type_of(recv), frame_state.insn_idx) { - ReceiverTypeResolution::StaticallyKnown { class } => (class, None), - ReceiverTypeResolution::Monomorphic { profiled_type } - | ReceiverTypeResolution::SkewedPolymorphic { profiled_type } => (profiled_type.class(), Some(profiled_type)), - ReceiverTypeResolution::SkewedMegamorphic { .. } - | ReceiverTypeResolution::Megamorphic => { - if get_option!(stats) { - self.set_dynamic_send_reason(insn_id, SendMegamorphic); - } - self.push_insn_id(block, insn_id); - continue; - } - ReceiverTypeResolution::Polymorphic => { - if get_option!(stats) { - self.set_dynamic_send_reason(insn_id, SendPolymorphic); - } - self.push_insn_id(block, insn_id); - continue; - } - ReceiverTypeResolution::NoProfile => { - if get_option!(stats) { - self.set_dynamic_send_reason(insn_id, SendNoProfiles); - } - self.push_insn_id(block, insn_id); - continue; - } - }; - let ci = unsafe { get_call_data_ci(cd) }; // info about the call site - - let flags = unsafe { rb_vm_ci_flag(ci) }; - - let mid = unsafe { vm_ci_mid(ci) }; - // Do method lookup - let mut cme = unsafe { rb_callable_method_entry(klass, mid) }; - if cme.is_null() { - self.set_dynamic_send_reason(insn_id, SendNotOptimizedMethodType(MethodType::Null)); - self.push_insn_id(block, insn_id); continue; - } - // Load an overloaded cme if applicable. See vm_search_cc(). - // It allows you to use a faster ISEQ if possible. - cme = unsafe { rb_check_overloaded_cme(cme, ci) }; - let visibility = unsafe { METHOD_ENTRY_VISI(cme) }; - match (visibility, flags & VM_CALL_FCALL != 0) { - (METHOD_VISI_PUBLIC, _) => {} - (METHOD_VISI_PRIVATE, true) => {} - (METHOD_VISI_PROTECTED, true) => {} - _ => { - self.set_dynamic_send_reason(insn_id, SendNotOptimizedNeedPermission); - self.push_insn_id(block, insn_id); continue; - } - } - let mut def_type = unsafe { get_cme_def_type(cme) }; - while def_type == VM_METHOD_TYPE_ALIAS { - cme = unsafe { rb_aliased_callable_method_entry(cme) }; - def_type = unsafe { get_cme_def_type(cme) }; - } - - // If the call site info indicates that the `Function` has overly complex arguments, then do not optimize into a `SendDirect`. - // Optimized methods(`VM_METHOD_TYPE_OPTIMIZED`) handle their own argument constraints (e.g., kw_splat for Proc call). - if def_type != VM_METHOD_TYPE_OPTIMIZED && unspecializable_call_type(flags) { - self.count_complex_call_features(block, flags); - self.set_dynamic_send_reason(insn_id, ComplexArgPass); - self.push_insn_id(block, insn_id); continue; - } - - if def_type == VM_METHOD_TYPE_ISEQ { - let iseq = unsafe { get_def_iseq_ptr((*cme).def) }; - if !can_direct_send(self, block, iseq, ci, insn_id, args.as_slice(), Some(blockiseq)) { - self.push_insn_id(block, insn_id); continue; - } - - // Check singleton class assumption first, before emitting other patchpoints - if !self.assume_no_singleton_classes(block, klass, state) { - self.set_dynamic_send_reason(insn_id, SingletonClassSeen); - self.push_insn_id(block, insn_id); continue; - } - - // Add PatchPoint for method redefinition - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); - - // Add GuardType for profiled receiver - if let Some(profiled_type) = profiled_type { - recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); - } - - let Ok((send_state, processed_args, kw_bits)) = self.prepare_direct_send_args(block, &args, ci, iseq, state) - .inspect_err(|&reason| self.set_dynamic_send_reason(insn_id, reason)) else { - self.push_insn_id(block, insn_id); continue; - }; - - let send_direct = self.push_insn(block, Insn::SendDirect { - recv, - cd, - cme, - iseq, - args: processed_args, - kw_bits, - blockiseq: Some(blockiseq), - state: send_state, - }); - self.make_equal_to(insn_id, send_direct); - } else { - self.set_dynamic_send_reason(insn_id, SendNotOptimizedMethodType(MethodType::from(def_type))); + let reason = if has_block { SendNotOptimizedMethodType(MethodType::from(def_type)) } else { SendWithoutBlockNotOptimizedMethodType(MethodType::from(def_type)) }; + self.set_dynamic_send_reason(insn_id, reason); self.push_insn_id(block, insn_id); continue; } } @@ -3440,7 +3318,7 @@ impl Function { } Insn::ObjToString { val, cd, state, .. } => { if self.is_a(val, types::String) { - // behaves differently from `SendWithoutBlock` with `mid:to_s` because ObjToString should not have a patch point for String to_s being redefined + // behaves differently from `Send` with `mid:to_s` because ObjToString should not have a patch point for String to_s being redefined self.make_equal_to(insn_id, val); continue; } @@ -3457,7 +3335,7 @@ impl Function { self.make_equal_to(insn_id, guard); } else { let recv = self.push_insn(block, Insn::GuardType { val, guard_type: Type::from_profiled_type(recv_type), state}); - let send_to_s = self.push_insn(block, Insn::SendWithoutBlock { recv, cd, args: vec![], state, reason: ObjToStringNotString }); + let send_to_s = self.push_insn(block, Insn::Send { recv, cd, blockiseq: None, args: vec![], state, reason: ObjToStringNotString }); self.make_equal_to(insn_id, send_to_s); } } @@ -4066,7 +3944,7 @@ impl Function { self.push_insn(block, Insn::IncrCounterPtr { counter_ptr }); } - /// Optimize Send/SendWithoutBlock that land in a C method to a direct CCall without + /// Optimize Send that land in a C method to a direct CCall without /// runtime lookup. fn optimize_c_calls(&mut self) { if unsafe { rb_zjit_method_tracing_currently_enabled() } { @@ -4139,7 +4017,11 @@ impl Function { return Err(()); } - let blockiseq = if blockiseq.is_null() { None } else { Some(blockiseq) }; + let blockiseq = match blockiseq { + None => unreachable!("went to reduce_send_without_block_to_ccall"), + Some(p) if p.is_null() => None, + Some(blockiseq) => Some(blockiseq), + }; let cfunc = unsafe { get_cme_def_body_cfunc(cme) }; // Find the `argc` (arity) of the C method, which describes the parameters it expects @@ -4236,7 +4118,7 @@ impl Function { } } - // Try to reduce a SendWithoutBlock insn to a CCall/CCallWithFrame + // Try to reduce a Send insn with blockiseq: None to a CCall/CCallWithFrame fn reduce_send_without_block_to_ccall( fun: &mut Function, block: BlockId, @@ -4244,7 +4126,7 @@ impl Function { send: Insn, send_insn_id: InsnId, ) -> Result<(), ()> { - let Insn::SendWithoutBlock { mut recv, cd, args, state, .. } = send else { + let Insn::Send { mut recv, cd, args, state, .. } = send else { return Err(()); }; @@ -4474,7 +4356,7 @@ impl Function { for insn_id in old_insns { let send = self.find(insn_id); match send { - send @ Insn::SendWithoutBlock { recv, .. } => { + send @ Insn::Send { recv, blockiseq: None, .. } => { let recv_type = self.type_of(recv); if reduce_send_without_block_to_ccall(self, block, recv_type, send, insn_id).is_ok() { continue; @@ -4877,7 +4759,6 @@ impl Function { } &Insn::Send { recv, ref args, state, .. } | &Insn::SendForward { recv, ref args, state, .. } - | &Insn::SendWithoutBlock { recv, ref args, state, .. } | &Insn::CCallVariadic { recv, ref args, state, .. } | &Insn::CCallWithFrame { recv, ref args, state, .. } | &Insn::SendDirect { recv, ref args, state, .. } @@ -5537,8 +5418,7 @@ impl Function { self.assert_subtype(insn_id, allow_nil, types::BoolExact) } // Instructions with recv and a Vec of Ruby objects - Insn::SendWithoutBlock { recv, ref args, .. } - | Insn::SendDirect { recv, ref args, .. } + Insn::SendDirect { recv, ref args, .. } | Insn::Send { recv, ref args, .. } | Insn::SendForward { recv, ref args, .. } | Insn::InvokeSuper { recv, ref args, .. } @@ -7030,7 +6910,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let args = state.stack_pop_n(argc as usize)?; let recv = state.stack_pop()?; - let send = fun.push_insn(block, Insn::SendWithoutBlock { recv, cd, args, state: exit_id, reason: Uncategorized(opcode) }); + let send = fun.push_insn(block, Insn::Send { recv, cd, blockiseq: None, args, state: exit_id, reason: Uncategorized(opcode) }); state.stack_push(send); } YARVINSN_opt_hash_freeze => { @@ -7153,7 +7033,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let recv = state.stack_pop().unwrap(); let refined_recv = fun.push_insn(block, Insn::RefineType { val: recv, new_type }); state.replace(recv, refined_recv); - let send = fun.push_insn(block, Insn::SendWithoutBlock { recv: refined_recv, cd, args, state: snapshot, reason: Uncategorized(opcode) }); + let send = fun.push_insn(block, Insn::Send { recv: refined_recv, cd, blockiseq: None, args, state: snapshot, reason: Uncategorized(opcode) }); state.stack_push(send); fun.push_insn(block, Insn::Jump(BranchEdge { target: join_block, args: state.as_args(self_param) })); block @@ -7186,7 +7066,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let args = state.stack_pop_n(argc as usize)?; let recv = state.stack_pop()?; let reason = SendWithoutBlockPolymorphicFallback; - let send = fun.push_insn(block, Insn::SendWithoutBlock { recv, cd, args, state: exit_id, reason }); + let send = fun.push_insn(block, Insn::Send { recv, cd, blockiseq: None, args, state: exit_id, reason }); state.stack_push(send); fun.push_insn(block, Insn::Jump(BranchEdge { target: join_block, args: state.as_args(self_param) })); break; // End the block @@ -7195,7 +7075,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let args = state.stack_pop_n(argc as usize)?; let recv = state.stack_pop()?; - let send = fun.push_insn(block, Insn::SendWithoutBlock { recv, cd, args, state: exit_id, reason: Uncategorized(opcode) }); + let send = fun.push_insn(block, Insn::Send { recv, cd, blockiseq: None, args, state: exit_id, reason: Uncategorized(opcode) }); state.stack_push(send); } YARVINSN_send => { @@ -7213,7 +7093,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { let args = state.stack_pop_n(argc as usize + usize::from(block_arg))?; let recv = state.stack_pop()?; - let send = fun.push_insn(block, Insn::Send { recv, cd, blockiseq, args, state: exit_id, reason: Uncategorized(opcode) }); + let send = fun.push_insn(block, Insn::Send { recv, cd, blockiseq: Some(blockiseq), args, state: exit_id, reason: Uncategorized(opcode) }); state.stack_push(send); if !blockiseq.is_null() { diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index d32de872d5..1315555694 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -863,6 +863,37 @@ mod hir_opt_tests { "); } + // Regression test: when specialized_instruction is disabled, the compiler + // doesn't convert `send` to `opt_send_without_block`, so a no-block call + // reaches ZJIT as `YARVINSN_send` with a null blockiseq. This becomes + // `Send { blockiseq: Some(null_ptr) }` which must be normalized to None in + // reduce_send_to_ccall, otherwise CCallWithFrame gens wrong block handler. + #[test] + fn test_send_to_cfunc_without_specialized_instruction() { + eval_with_options(" + def test(a) = a.length + test([1,2,3]); test([1,2,3]) + ", "{ specialized_instruction: false }"); + assert_snapshot!(hir_string("test"), @r" + fn test@<compiled>:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :a, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) + v22:ArrayExact = GuardType v9, ArrayExact + v23:BasicObject = CCallWithFrame v22, :Array#length@0x1038 + CheckInterrupts + Return v23 + "); + } + #[test] fn test_optimize_nonexistent_top_level_call() { eval(" @@ -884,7 +915,7 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - v11:BasicObject = SendWithoutBlock v6, :foo # SendFallbackReason: SendWithoutBlock: unsupported method type Null + v11:BasicObject = Send v6, :foo # SendFallbackReason: SendWithoutBlock: unsupported method type Null CheckInterrupts Return v11 "); @@ -1126,7 +1157,7 @@ mod hir_opt_tests { v28:Fixnum[30] = Const Value(30) v30:Fixnum[40] = Const Value(40) v32:Fixnum[50] = Const Value(50) - v34:BasicObject = SendWithoutBlock v6, :target, v24, v26, v28, v30, v32 # SendFallbackReason: Argument count does not match parameter count + v34:BasicObject = Send v6, :target, v24, v26, v28, v30, v32 # SendFallbackReason: Argument count does not match parameter count v37:ArrayExact = NewArray v45, v49, v34 CheckInterrupts Return v37 @@ -2689,7 +2720,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) v12:Fixnum[0] = Const Value(0) - v14:BasicObject = SendWithoutBlock v10, :itself, v12 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc + v14:BasicObject = Send v10, :itself, v12 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc CheckInterrupts Return v14 "); @@ -2984,7 +3015,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[1] = Const Value(1) IncrCounter complex_arg_pass_param_rest - v13:BasicObject = SendWithoutBlock v6, :foo, v11 # SendFallbackReason: Complex argument passing + v13:BasicObject = Send v6, :foo, v11 # SendFallbackReason: Complex argument passing CheckInterrupts Return v13 "); @@ -3009,7 +3040,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[10] = Const Value(10) IncrCounter complex_arg_pass_param_post - v13:BasicObject = SendWithoutBlock v6, :foo, v11 # SendFallbackReason: Complex argument passing + v13:BasicObject = Send v6, :foo, v11 # SendFallbackReason: Complex argument passing CheckInterrupts Return v13 "); @@ -3248,7 +3279,7 @@ mod hir_opt_tests { v33:Fixnum[40] = Const Value(40) v35:Fixnum[50] = Const Value(50) v37:Fixnum[60] = Const Value(60) - v39:BasicObject = SendWithoutBlock v6, :target, v27, v29, v31, v33, v35, v37 # SendFallbackReason: Too many arguments for LIR + v39:BasicObject = Send v6, :target, v27, v29, v31, v33, v35, v37 # SendFallbackReason: Too many arguments for LIR v41:ArrayExact = NewArray v49, v53, v39 CheckInterrupts Return v41 @@ -3303,7 +3334,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[1] = Const Value(1) IncrCounter complex_arg_pass_param_kwrest - v13:BasicObject = SendWithoutBlock v6, :foo, v11 # SendFallbackReason: Complex argument passing + v13:BasicObject = Send v6, :foo, v11 # SendFallbackReason: Complex argument passing CheckInterrupts Return v13 "); @@ -3358,7 +3389,7 @@ mod hir_opt_tests { v11:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v12:HashExact = HashDup v11 IncrCounter complex_arg_pass_caller_kw_splat - v14:BasicObject = SendWithoutBlock v6, :foo, v12 # SendFallbackReason: Complex argument passing + v14:BasicObject = Send v6, :foo, v12 # SendFallbackReason: Complex argument passing CheckInterrupts Return v14 "); @@ -3383,7 +3414,7 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): IncrCounter complex_arg_pass_param_kwrest - v11:BasicObject = SendWithoutBlock v6, :foo # SendFallbackReason: Complex argument passing + v11:BasicObject = Send v6, :foo # SendFallbackReason: Complex argument passing CheckInterrupts Return v11 "); @@ -3410,7 +3441,7 @@ mod hir_opt_tests { v12:StringExact = StringCopy v11 v14:Fixnum[1] = Const Value(1) IncrCounter complex_arg_pass_caller_kwarg - v16:BasicObject = SendWithoutBlock v6, :sprintf, v12, v14 # SendFallbackReason: Complex argument passing + v16:BasicObject = Send v6, :sprintf, v12, v14 # SendFallbackReason: Complex argument passing CheckInterrupts Return v16 "); @@ -3707,7 +3738,7 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Hash@0x1008, new@0x1009, cme:0x1010) v46:HashExact = ObjectAllocClass Hash:VALUE(0x1008) IncrCounter complex_arg_pass_param_block - v20:BasicObject = SendWithoutBlock v46, :initialize # SendFallbackReason: Complex argument passing + v20:BasicObject = Send v46, :initialize # SendFallbackReason: Complex argument passing CheckInterrupts CheckInterrupts Return v46 @@ -3917,7 +3948,7 @@ mod hir_opt_tests { v17:CInt64 = LoadField v14, :_env_data_index_specval@0x1001 v18:CInt64 = GuardAnyBitSet v17, CUInt64(1) v19:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1008)) - v21:BasicObject = Send v8, 0x1010, :tap, v19 # SendFallbackReason: Uncategorized(send) + v21:BasicObject = Send v8, 0x1000, :tap, v19 # SendFallbackReason: Uncategorized(send) CheckInterrupts Return v21 "); @@ -4228,7 +4259,7 @@ mod hir_opt_tests { v16:ArrayExact = NewArray v22:ArrayExact = ToArray v16 IncrCounter complex_arg_pass_caller_splat - v24:BasicObject = SendWithoutBlock v11, :call, v22 # SendFallbackReason: Complex argument passing + v24:BasicObject = Send v11, :call, v22 # SendFallbackReason: Complex argument passing CheckInterrupts Return v24 "); @@ -4256,7 +4287,7 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v14:Fixnum[1] = Const Value(1) IncrCounter complex_arg_pass_caller_kwarg - v16:BasicObject = SendWithoutBlock v9, :call, v14 # SendFallbackReason: Complex argument passing + v16:BasicObject = Send v9, :call, v14 # SendFallbackReason: Complex argument passing CheckInterrupts Return v16 "); @@ -4673,7 +4704,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Hash@0x1000) PatchPoint MethodRedefined(Hash@0x1000, dup@0x1008, cme:0x1010) v22:BasicObject = CCallWithFrame v10, :Kernel#dup@0x1038 - v14:BasicObject = SendWithoutBlock v22, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) + v14:BasicObject = Send v22, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v14 "); @@ -4696,7 +4727,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:HashExact = NewHash v12:NilClass = Const Value(nil) - v14:BasicObject = SendWithoutBlock v10, :freeze, v12 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc + v14:BasicObject = Send v10, :freeze, v12 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc CheckInterrupts Return v14 "); @@ -4766,7 +4797,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, dup@0x1008, cme:0x1010) v22:BasicObject = CCallWithFrame v10, :Kernel#dup@0x1038 - v14:BasicObject = SendWithoutBlock v22, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) + v14:BasicObject = Send v22, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v14 "); @@ -4789,7 +4820,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:ArrayExact = NewArray v12:NilClass = Const Value(nil) - v14:BasicObject = SendWithoutBlock v10, :freeze, v12 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc + v14:BasicObject = Send v10, :freeze, v12 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc CheckInterrupts Return v14 "); @@ -4860,7 +4891,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) v23:BasicObject = CCallWithFrame v11, :String#dup@0x1040 - v15:BasicObject = SendWithoutBlock v23, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) + v15:BasicObject = Send v23, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v15 "); @@ -4884,7 +4915,7 @@ mod hir_opt_tests { v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 v13:NilClass = Const Value(nil) - v15:BasicObject = SendWithoutBlock v11, :freeze, v13 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc + v15:BasicObject = Send v11, :freeze, v13 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc CheckInterrupts Return v15 "); @@ -4955,7 +4986,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) v23:BasicObject = CCallWithFrame v11, :String#dup@0x1040 - v15:BasicObject = SendWithoutBlock v23, :-@ # SendFallbackReason: Uncategorized(opt_send_without_block) + v15:BasicObject = Send v23, :-@ # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v15 "); @@ -5381,7 +5412,7 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v16:Fixnum[1] = Const Value(1) v18:Fixnum[10] = Const Value(10) - v22:BasicObject = SendWithoutBlock v9, :[]=, v16, v18 # SendFallbackReason: Uncategorized(opt_aset) + v22:BasicObject = Send v9, :[]=, v16, v18 # SendFallbackReason: Uncategorized(opt_aset) CheckInterrupts Return v18 "); @@ -5517,7 +5548,7 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v11:Fixnum[100] = Const Value(100) - v13:BasicObject = SendWithoutBlock v6, :identity, v11 # SendFallbackReason: Bmethod: Proc object is not defined by an ISEQ + v13:BasicObject = Send v6, :identity, v11 # SendFallbackReason: Bmethod: Proc object is not defined by an ISEQ CheckInterrupts Return v13 "); @@ -6389,7 +6420,7 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - v11:BasicObject = SendWithoutBlock v6, :foo # SendFallbackReason: Uncategorized(opt_send_without_block) + v11:BasicObject = Send v6, :foo # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v11 "); @@ -6436,7 +6467,7 @@ mod hir_opt_tests { IfTrue v14, bb4(v8, v9, v9) v23:CBool = HasType v9, HeapObject[class_exact:C] IfTrue v23, bb5(v8, v9, v9) - v32:BasicObject = SendWithoutBlock v9, :foo # SendFallbackReason: SendWithoutBlock: polymorphic fallback + v32:BasicObject = Send v9, :foo # SendFallbackReason: SendWithoutBlock: polymorphic fallback Jump bb3(v8, v9, v32) bb4(v15:BasicObject, v16:BasicObject, v17:BasicObject): v19:HeapObject[class_exact:C] = RefineType v17, HeapObject[class_exact:C] @@ -6589,7 +6620,7 @@ mod hir_opt_tests { v19:CInt64 = GuardAnyBitSet v18, CUInt64(1) v20:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1008)) IncrCounter complex_arg_pass_caller_blockarg - v22:BasicObject = Send v13, 0x1010, :map, v20 # SendFallbackReason: Complex argument passing + v22:BasicObject = Send v13, 0x1000, :map, v20 # SendFallbackReason: Complex argument passing CheckInterrupts Return v22 "); @@ -6620,7 +6651,7 @@ mod hir_opt_tests { v19:CInt64[0] = GuardBitEquals v18, CInt64(0) v20:NilClass = Const Value(nil) IncrCounter complex_arg_pass_caller_blockarg - v22:BasicObject = Send v13, 0x1008, :map, v20 # SendFallbackReason: Complex argument passing + v22:BasicObject = Send v13, 0x1000, :map, v20 # SendFallbackReason: Complex argument passing CheckInterrupts Return v22 "); @@ -6654,7 +6685,7 @@ mod hir_opt_tests { v16:CInt64 = GuardAnyBitSet v15, CUInt64(1) v17:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1008)) IncrCounter complex_arg_pass_caller_blockarg - v19:BasicObject = Send v10, 0x1010, :map, v17 # SendFallbackReason: Complex argument passing + v19:BasicObject = Send v10, 0x1000, :map, v17 # SendFallbackReason: Complex argument passing CheckInterrupts Return v19 "); @@ -8609,7 +8640,7 @@ mod hir_opt_tests { v12:Fixnum[3] = Const Value(3) v14:Fixnum[3] = Const Value(3) v16:Fixnum[3] = Const Value(3) - v18:BasicObject = SendWithoutBlock v10, :foo, v12, v14, v16 # SendFallbackReason: Argument count does not match parameter count + v18:BasicObject = Send v10, :foo, v12, v14, v16 # SendFallbackReason: Argument count does not match parameter count CheckInterrupts Return v18 "); @@ -8659,7 +8690,7 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[0] = Const Value(0) - v12:BasicObject = SendWithoutBlock v10, :foo # SendFallbackReason: Argument count does not match parameter count + v12:BasicObject = Send v10, :foo # SendFallbackReason: Argument count does not match parameter count CheckInterrupts Return v12 "); @@ -8682,7 +8713,7 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:Fixnum[4] = Const Value(4) v12:Fixnum[1] = Const Value(1) - v14:BasicObject = SendWithoutBlock v10, :succ, v12 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc + v14:BasicObject = Send v10, :succ, v12 # SendFallbackReason: SendWithoutBlock: unsupported method type Cfunc CheckInterrupts Return v14 "); @@ -8836,7 +8867,7 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v17:BasicObject = SendWithoutBlock v11, :^ # SendFallbackReason: Uncategorized(opt_send_without_block) + v17:BasicObject = Send v11, :^ # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v17 "); @@ -9511,17 +9542,17 @@ mod hir_opt_tests { v13:ArrayExact = NewArray v19:ArrayExact = ToArray v13 IncrCounter complex_arg_pass_caller_splat - v21:BasicObject = SendWithoutBlock v8, :foo, v19 # SendFallbackReason: Complex argument passing + v21:BasicObject = Send v8, :foo, v19 # SendFallbackReason: Complex argument passing v25:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v26:StringExact = StringCopy v25 PatchPoint NoEPEscape(test) v31:ArrayExact = ToArray v13 IncrCounter complex_arg_pass_caller_splat - v33:BasicObject = SendWithoutBlock v26, :display, v31 # SendFallbackReason: Complex argument passing + v33:BasicObject = Send v26, :display, v31 # SendFallbackReason: Complex argument passing PatchPoint NoEPEscape(test) v41:ArrayExact = ToArray v13 IncrCounter complex_arg_pass_caller_splat - v43:BasicObject = SendWithoutBlock v8, :itself, v41 # SendFallbackReason: Complex argument passing + v43:BasicObject = Send v8, :itself, v41 # SendFallbackReason: Complex argument passing CheckInterrupts Return v43 "); @@ -10322,7 +10353,7 @@ mod hir_opt_tests { IncrCounter complex_arg_pass_param_rest IncrCounter complex_arg_pass_param_block IncrCounter complex_arg_pass_param_kwrest - v13:BasicObject = SendWithoutBlock v6, :fancy, v11 # SendFallbackReason: Complex argument passing + v13:BasicObject = Send v6, :fancy, v11 # SendFallbackReason: Complex argument passing CheckInterrupts Return v13 "); @@ -10346,7 +10377,7 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): IncrCounter complex_arg_pass_param_forwardable - v11:BasicObject = SendWithoutBlock v6, :forwardable # SendFallbackReason: Complex argument passing + v11:BasicObject = Send v6, :forwardable # SendFallbackReason: Complex argument passing CheckInterrupts Return v11 "); @@ -11081,7 +11112,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Obj) v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:BasicObject = SendWithoutBlock v21, :secret # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL + v13:BasicObject = Send v21, :secret # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL CheckInterrupts Return v13 "); @@ -11135,7 +11166,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Obj) v21:BasicObjectExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:BasicObject = SendWithoutBlock v21, :initialize # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL + v13:BasicObject = Send v21, :initialize # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL CheckInterrupts Return v13 "); @@ -11162,7 +11193,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Obj) v21:ObjectExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:BasicObject = SendWithoutBlock v21, :toplevel_method # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL + v13:BasicObject = Send v21, :toplevel_method # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL CheckInterrupts Return v13 "); @@ -11220,7 +11251,7 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Obj) v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:BasicObject = SendWithoutBlock v21, :secret # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL + v13:BasicObject = Send v21, :secret # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL CheckInterrupts Return v13 "); @@ -11259,7 +11290,7 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v15:BasicObject = SendWithoutBlock v9, :length # SendFallbackReason: Singleton class previously created for receiver class + v15:BasicObject = Send v9, :length # SendFallbackReason: Singleton class previously created for receiver class CheckInterrupts Return v15 "); @@ -11783,7 +11814,7 @@ mod hir_opt_tests { IfTrue v14, bb4(v8, v9, v9) v23:CBool = HasType v9, HeapObject[class_exact:D] IfTrue v23, bb5(v8, v9, v9) - v32:BasicObject = SendWithoutBlock v9, :foo # SendFallbackReason: SendWithoutBlock: polymorphic fallback + v32:BasicObject = Send v9, :foo # SendFallbackReason: SendWithoutBlock: polymorphic fallback Jump bb3(v8, v9, v32) bb4(v15:BasicObject, v16:BasicObject, v17:BasicObject): PatchPoint NoSingletonClass(C@0x1000) @@ -11835,7 +11866,7 @@ mod hir_opt_tests { IfTrue v14, bb4(v8, v9, v9) v23:CBool = HasType v9, Fixnum IfTrue v23, bb5(v8, v9, v9) - v32:BasicObject = SendWithoutBlock v9, :itself # SendFallbackReason: SendWithoutBlock: polymorphic fallback + v32:BasicObject = Send v9, :itself # SendFallbackReason: SendWithoutBlock: polymorphic fallback Jump bb3(v8, v9, v32) bb4(v15:BasicObject, v16:BasicObject, v17:BasicObject): v19:HeapObject[class_exact:C] = RefineType v17, HeapObject[class_exact:C] diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 0401ffcd7d..8b15d7d23f 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -151,7 +151,7 @@ mod snapshot_tests { v23:Fixnum[7] = Const Value(7) v25:Fixnum[8] = Const Value(8) v26:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v11, v13, v15, v17, v19, v21, v23, v25], locals: [] } - v27:BasicObject = SendWithoutBlock v6, :foo, v11, v13, v15, v17, v19, v21, v23, v25 # SendFallbackReason: Too many arguments for LIR + v27:BasicObject = Send v6, :foo, v11, v13, v15, v17, v19, v21, v23, v25 # SendFallbackReason: Too many arguments for LIR v28:Any = Snapshot FrameState { pc: 0x1010, stack: [v27], locals: [] } PatchPoint NoTracePoint CheckInterrupts @@ -641,7 +641,7 @@ pub mod hir_build_tests { bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) v12:Fixnum[2] = Const Value(2) - v15:BasicObject = SendWithoutBlock v10, :+, v12 # SendFallbackReason: Uncategorized(opt_plus) + v15:BasicObject = Send v10, :+, v12 # SendFallbackReason: Uncategorized(opt_plus) CheckInterrupts Return v15 "); @@ -894,11 +894,11 @@ pub mod hir_build_tests { SetLocal :l1, l1, EP@3, v10 v15:BasicObject = GetLocal :l1, l1, EP@3 v17:BasicObject = GetLocal :l2, l2, EP@4 - v20:BasicObject = SendWithoutBlock v15, :+, v17 # SendFallbackReason: Uncategorized(opt_plus) + v20:BasicObject = Send v15, :+, v17 # SendFallbackReason: Uncategorized(opt_plus) SetLocal :l2, l2, EP@4, v20 v25:BasicObject = GetLocal :l2, l2, EP@4 v27:BasicObject = GetLocal :l3, l3, EP@5 - v30:BasicObject = SendWithoutBlock v25, :+, v27 # SendFallbackReason: Uncategorized(opt_plus) + v30:BasicObject = Send v25, :+, v27 # SendFallbackReason: Uncategorized(opt_plus) SetLocal :l3, l3, EP@5, v30 CheckInterrupts Return v30 @@ -1268,7 +1268,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :+, v12 # SendFallbackReason: Uncategorized(opt_plus) + v19:BasicObject = Send v11, :+, v12 # SendFallbackReason: Uncategorized(opt_plus) CheckInterrupts Return v19 "); @@ -1293,7 +1293,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :-, v12 # SendFallbackReason: Uncategorized(opt_minus) + v19:BasicObject = Send v11, :-, v12 # SendFallbackReason: Uncategorized(opt_minus) CheckInterrupts Return v19 "); @@ -1318,7 +1318,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :*, v12 # SendFallbackReason: Uncategorized(opt_mult) + v19:BasicObject = Send v11, :*, v12 # SendFallbackReason: Uncategorized(opt_mult) CheckInterrupts Return v19 "); @@ -1343,7 +1343,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :/, v12 # SendFallbackReason: Uncategorized(opt_div) + v19:BasicObject = Send v11, :/, v12 # SendFallbackReason: Uncategorized(opt_div) CheckInterrupts Return v19 "); @@ -1368,7 +1368,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :%, v12 # SendFallbackReason: Uncategorized(opt_mod) + v19:BasicObject = Send v11, :%, v12 # SendFallbackReason: Uncategorized(opt_mod) CheckInterrupts Return v19 "); @@ -1393,7 +1393,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :==, v12 # SendFallbackReason: Uncategorized(opt_eq) + v19:BasicObject = Send v11, :==, v12 # SendFallbackReason: Uncategorized(opt_eq) CheckInterrupts Return v19 "); @@ -1418,7 +1418,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :!=, v12 # SendFallbackReason: Uncategorized(opt_neq) + v19:BasicObject = Send v11, :!=, v12 # SendFallbackReason: Uncategorized(opt_neq) CheckInterrupts Return v19 "); @@ -1443,7 +1443,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :<, v12 # SendFallbackReason: Uncategorized(opt_lt) + v19:BasicObject = Send v11, :<, v12 # SendFallbackReason: Uncategorized(opt_lt) CheckInterrupts Return v19 "); @@ -1468,7 +1468,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :<=, v12 # SendFallbackReason: Uncategorized(opt_le) + v19:BasicObject = Send v11, :<=, v12 # SendFallbackReason: Uncategorized(opt_le) CheckInterrupts Return v19 "); @@ -1493,7 +1493,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :>, v12 # SendFallbackReason: Uncategorized(opt_gt) + v19:BasicObject = Send v11, :>, v12 # SendFallbackReason: Uncategorized(opt_gt) CheckInterrupts Return v19 "); @@ -1533,7 +1533,7 @@ pub mod hir_build_tests { Jump bb4(v10, v16, v20) bb4(v26:BasicObject, v27:BasicObject, v28:BasicObject): v32:Fixnum[0] = Const Value(0) - v35:BasicObject = SendWithoutBlock v28, :>, v32 # SendFallbackReason: Uncategorized(opt_gt) + v35:BasicObject = Send v28, :>, v32 # SendFallbackReason: Uncategorized(opt_gt) CheckInterrupts v38:CBool = Test v35 v39:Truthy = RefineType v35, Truthy @@ -1544,9 +1544,9 @@ pub mod hir_build_tests { Return v27 bb3(v51:BasicObject, v52:BasicObject, v53:BasicObject): v58:Fixnum[1] = Const Value(1) - v61:BasicObject = SendWithoutBlock v52, :+, v58 # SendFallbackReason: Uncategorized(opt_plus) + v61:BasicObject = Send v52, :+, v58 # SendFallbackReason: Uncategorized(opt_plus) v66:Fixnum[1] = Const Value(1) - v69:BasicObject = SendWithoutBlock v53, :-, v66 # SendFallbackReason: Uncategorized(opt_minus) + v69:BasicObject = Send v53, :-, v66 # SendFallbackReason: Uncategorized(opt_minus) Jump bb4(v51, v61, v69) "); } @@ -1570,7 +1570,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :>=, v12 # SendFallbackReason: Uncategorized(opt_ge) + v19:BasicObject = Send v11, :>=, v12 # SendFallbackReason: Uncategorized(opt_ge) CheckInterrupts Return v19 "); @@ -1639,7 +1639,7 @@ pub mod hir_build_tests { bb2(v6:BasicObject): v11:Fixnum[2] = Const Value(2) v13:Fixnum[3] = Const Value(3) - v15:BasicObject = SendWithoutBlock v6, :bar, v11, v13 # SendFallbackReason: Uncategorized(opt_send_without_block) + v15:BasicObject = Send v6, :bar, v11, v13 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v15 "); @@ -1726,7 +1726,7 @@ pub mod hir_build_tests { v18:StringExact = StringCopy v17 v20:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) v21:StringExact = StringCopy v20 - v23:BasicObject = SendWithoutBlock v6, :unknown_method, v12, v15, v18, v21 # SendFallbackReason: Uncategorized(opt_send_without_block) + v23:BasicObject = Send v6, :unknown_method, v12, v15, v18, v21 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v23 "); @@ -1749,7 +1749,7 @@ pub mod hir_build_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v15:ArrayExact = ToArray v9 - v17:BasicObject = SendWithoutBlock v8, :foo, v15 # SendFallbackReason: Uncategorized(opt_send_without_block) + v17:BasicObject = Send v8, :foo, v15 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v17 "); @@ -1794,7 +1794,7 @@ pub mod hir_build_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:Fixnum[1] = Const Value(1) - v16:BasicObject = SendWithoutBlock v8, :foo, v14 # SendFallbackReason: Uncategorized(opt_send_without_block) + v16:BasicObject = Send v8, :foo, v14 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v16 "); @@ -1816,7 +1816,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v15:BasicObject = SendWithoutBlock v8, :foo, v9 # SendFallbackReason: Uncategorized(opt_send_without_block) + v15:BasicObject = Send v8, :foo, v9 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v15 "); @@ -1951,7 +1951,7 @@ pub mod hir_build_tests { bb2(v8:BasicObject, v9:BasicObject): v15:BasicObject = InvokeSuperForward v8, 0x1000, v9 # SendFallbackReason: Uncategorized(invokesuperforward) v17:Fixnum[1] = Const Value(1) - v20:BasicObject = SendWithoutBlock v15, :+, v17 # SendFallbackReason: Uncategorized(opt_plus) + v20:BasicObject = Send v15, :+, v17 # SendFallbackReason: Uncategorized(opt_plus) CheckInterrupts Return v20 "); @@ -2021,12 +2021,12 @@ pub mod hir_build_tests { v14:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) v16:HashExact = NewHash PatchPoint NoEPEscape(test) - v21:BasicObject = SendWithoutBlock v14, :core#hash_merge_kwd, v16, v9 # SendFallbackReason: Uncategorized(opt_send_without_block) + v21:BasicObject = Send v14, :core#hash_merge_kwd, v16, v9 # SendFallbackReason: Uncategorized(opt_send_without_block) v23:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) v26:StaticSymbol[:b] = Const Value(VALUE(0x1008)) v28:Fixnum[1] = Const Value(1) - v30:BasicObject = SendWithoutBlock v23, :core#hash_merge_ptr, v21, v26, v28 # SendFallbackReason: Uncategorized(opt_send_without_block) - v32:BasicObject = SendWithoutBlock v8, :foo, v30 # SendFallbackReason: Uncategorized(opt_send_without_block) + v30:BasicObject = Send v23, :core#hash_merge_ptr, v21, v26, v28 # SendFallbackReason: Uncategorized(opt_send_without_block) + v32:BasicObject = Send v8, :foo, v30 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v32 "); @@ -2051,7 +2051,7 @@ pub mod hir_build_tests { v15:ArrayExact = ToNewArray v9 v17:Fixnum[1] = Const Value(1) ArrayPush v15, v17 - v21:BasicObject = SendWithoutBlock v8, :foo, v15 # SendFallbackReason: Uncategorized(opt_send_without_block) + v21:BasicObject = Send v8, :foo, v15 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v21 "); @@ -2134,11 +2134,11 @@ pub mod hir_build_tests { v16:CBool = IsMethodCFunc v11, :new IfFalse v16, bb3(v6, v13, v11) v18:HeapBasicObject = ObjectAlloc v11 - v20:BasicObject = SendWithoutBlock v18, :initialize # SendFallbackReason: Uncategorized(opt_send_without_block) + v20:BasicObject = Send v18, :initialize # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Jump bb4(v6, v18, v20) bb3(v24:BasicObject, v25:NilClass, v26:BasicObject): - v29:BasicObject = SendWithoutBlock v26, :new # SendFallbackReason: Uncategorized(opt_send_without_block) + v29:BasicObject = Send v26, :new # SendFallbackReason: Uncategorized(opt_send_without_block) Jump bb4(v24, v29, v25) bb4(v32:BasicObject, v33:BasicObject, v34:BasicObject): CheckInterrupts @@ -2251,7 +2251,7 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) + v25:BasicObject = Send v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) SideExit UnhandledNewarraySend(MIN) "); } @@ -2283,13 +2283,13 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) + v25:BasicObject = Send v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_HASH) v32:Fixnum = ArrayHash v15, v16 PatchPoint NoEPEscape(test) v39:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v40:ArrayExact = ArrayDup v39 - v42:BasicObject = SendWithoutBlock v14, :puts, v40 # SendFallbackReason: Uncategorized(opt_send_without_block) + v42:BasicObject = Send v14, :puts, v40 # SendFallbackReason: Uncategorized(opt_send_without_block) PatchPoint NoEPEscape(test) CheckInterrupts Return v32 @@ -2325,7 +2325,7 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) + v25:BasicObject = Send v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_HASH)) "); } @@ -2357,7 +2357,7 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) + v25:BasicObject = Send v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) v31:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v32:StringExact = StringCopy v31 SideExit UnhandledNewarraySend(PACK) @@ -2391,7 +2391,7 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) + v25:BasicObject = Send v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) v29:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v30:StringExact = StringCopy v29 v36:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) @@ -2435,7 +2435,7 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) + v25:BasicObject = Send v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) v29:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v30:StringExact = StringCopy v29 v36:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) @@ -2472,13 +2472,13 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) + v25:BasicObject = Send v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_INCLUDE_P) v33:BoolExact = ArrayInclude v15, v16 | v16 PatchPoint NoEPEscape(test) v40:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v41:ArrayExact = ArrayDup v40 - v43:BasicObject = SendWithoutBlock v14, :puts, v41 # SendFallbackReason: Uncategorized(opt_send_without_block) + v43:BasicObject = Send v14, :puts, v41 # SendFallbackReason: Uncategorized(opt_send_without_block) PatchPoint NoEPEscape(test) CheckInterrupts Return v33 @@ -2519,7 +2519,7 @@ pub mod hir_build_tests { v12:NilClass = Const Value(nil) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): - v25:BasicObject = SendWithoutBlock v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) + v25:BasicObject = Send v15, :+, v16 # SendFallbackReason: Uncategorized(opt_plus) SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_INCLUDE_P)) "); } @@ -2598,7 +2598,7 @@ pub mod hir_build_tests { Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v18:ArrayExact = NewArray v11, v12 - v21:BasicObject = SendWithoutBlock v18, :length # SendFallbackReason: Uncategorized(opt_length) + v21:BasicObject = Send v18, :length # SendFallbackReason: Uncategorized(opt_length) CheckInterrupts Return v21 "); @@ -2623,7 +2623,7 @@ pub mod hir_build_tests { Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v18:ArrayExact = NewArray v11, v12 - v21:BasicObject = SendWithoutBlock v18, :size # SendFallbackReason: Uncategorized(opt_size) + v21:BasicObject = Send v18, :size # SendFallbackReason: Uncategorized(opt_size) CheckInterrupts Return v21 "); @@ -3020,7 +3020,7 @@ pub mod hir_build_tests { bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v16:NilClass = Const Value(nil) v20:Fixnum[1] = Const Value(1) - v24:BasicObject = SendWithoutBlock v11, :[]=, v12, v20 # SendFallbackReason: Uncategorized(opt_aset) + v24:BasicObject = Send v11, :[]=, v12, v20 # SendFallbackReason: Uncategorized(opt_aset) CheckInterrupts Return v20 "); @@ -3044,7 +3044,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :[], v12 # SendFallbackReason: Uncategorized(opt_aref) + v19:BasicObject = Send v11, :[], v12 # SendFallbackReason: Uncategorized(opt_aref) CheckInterrupts Return v19 "); @@ -3067,7 +3067,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v15:BasicObject = SendWithoutBlock v9, :empty? # SendFallbackReason: Uncategorized(opt_empty_p) + v15:BasicObject = Send v9, :empty? # SendFallbackReason: Uncategorized(opt_empty_p) CheckInterrupts Return v15 "); @@ -3090,7 +3090,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v15:BasicObject = SendWithoutBlock v9, :succ # SendFallbackReason: Uncategorized(opt_succ) + v15:BasicObject = Send v9, :succ # SendFallbackReason: Uncategorized(opt_succ) CheckInterrupts Return v15 "); @@ -3114,7 +3114,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :&, v12 # SendFallbackReason: Uncategorized(opt_and) + v19:BasicObject = Send v11, :&, v12 # SendFallbackReason: Uncategorized(opt_and) CheckInterrupts Return v19 "); @@ -3138,7 +3138,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :|, v12 # SendFallbackReason: Uncategorized(opt_or) + v19:BasicObject = Send v11, :|, v12 # SendFallbackReason: Uncategorized(opt_or) CheckInterrupts Return v19 "); @@ -3161,7 +3161,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - v15:BasicObject = SendWithoutBlock v9, :! # SendFallbackReason: Uncategorized(opt_not) + v15:BasicObject = Send v9, :! # SendFallbackReason: Uncategorized(opt_not) CheckInterrupts Return v15 "); @@ -3185,7 +3185,7 @@ pub mod hir_build_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v19:BasicObject = SendWithoutBlock v11, :=~, v12 # SendFallbackReason: Uncategorized(opt_regexpmatch2) + v19:BasicObject = Send v11, :=~, v12 # SendFallbackReason: Uncategorized(opt_regexpmatch2) CheckInterrupts Return v19 "); @@ -3215,7 +3215,7 @@ pub mod hir_build_tests { v12:BasicObject = PutSpecialObject CBase v14:StaticSymbol[:aliased] = Const Value(VALUE(0x1008)) v16:StaticSymbol[:__callee__] = Const Value(VALUE(0x1010)) - v18:BasicObject = SendWithoutBlock v10, :core#set_method_alias, v12, v14, v16 # SendFallbackReason: Uncategorized(opt_send_without_block) + v18:BasicObject = Send v10, :core#set_method_alias, v12, v14, v16 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v18 "); @@ -3317,7 +3317,7 @@ pub mod hir_build_tests { v17:NilClass = Const Value(nil) IfTrue v16, bb3(v8, v17, v17) v19:NotNil = RefineType v9, NotNil - v21:BasicObject = SendWithoutBlock v19, :itself # SendFallbackReason: Uncategorized(opt_send_without_block) + v21:BasicObject = Send v19, :itself # SendFallbackReason: Uncategorized(opt_send_without_block) Jump bb3(v8, v19, v21) bb3(v23:BasicObject, v24:BasicObject, v25:BasicObject): CheckInterrupts @@ -3359,7 +3359,7 @@ pub mod hir_build_tests { v25:NilClass = Const Value(nil) IfTrue v24, bb4(v8, v25, v25) v27:Truthy = RefineType v18, NotNil - v29:BasicObject = SendWithoutBlock v27, :itself # SendFallbackReason: Uncategorized(opt_send_without_block) + v29:BasicObject = Send v27, :itself # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v29 bb3(v34:BasicObject, v35:Falsy): @@ -3640,14 +3640,14 @@ pub mod hir_build_tests { v13:NilClass = Const Value(nil) v16:Fixnum[0] = Const Value(0) v18:Fixnum[1] = Const Value(1) - v21:BasicObject = SendWithoutBlock v9, :[], v16, v18 # SendFallbackReason: Uncategorized(opt_send_without_block) + v21:BasicObject = Send v9, :[], v16, v18 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts v25:CBool = Test v21 v26:Truthy = RefineType v21, Truthy IfTrue v25, bb3(v8, v9, v13, v9, v16, v18, v26) v28:Falsy = RefineType v21, Falsy v31:Fixnum[2] = Const Value(2) - v34:BasicObject = SendWithoutBlock v9, :[]=, v16, v18, v31 # SendFallbackReason: Uncategorized(opt_send_without_block) + v34:BasicObject = Send v9, :[]=, v16, v18, v31 # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts Return v31 bb3(v40:BasicObject, v41:BasicObject, v42:NilClass, v43:BasicObject, v44:Fixnum[0], v45:Fixnum[1], v46:Truthy): @@ -4001,7 +4001,7 @@ pub mod hir_build_tests { v21:FalseClass = RefineType v15, Falsy v23:Fixnum[1] = Const Value(1) v25:Fixnum[1] = Const Value(1) - v28:BasicObject = SendWithoutBlock v23, :+, v25 # SendFallbackReason: Uncategorized(opt_plus) + v28:BasicObject = Send v23, :+, v25 # SendFallbackReason: Uncategorized(opt_plus) Jump bb3(v10, v28, v12) bb3(v31:BasicObject, v32:BasicObject, v33:BasicObject): CheckInterrupts |
