summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compile.c124
-rw-r--r--test/ruby/test_pattern_matching.rb9
2 files changed, 74 insertions, 59 deletions
diff --git a/compile.c b/compile.c
index 0bdb7eea13..a28c023ef0 100644
--- a/compile.c
+++ b/compile.c
@@ -5611,6 +5611,8 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
static int iseq_compile_pattern_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *unmatched, int in_alt_pattern, int deconstructed_pos);
+static int iseq_compile_array_deconstruct(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *deconstruct, LABEL *deconstructed, LABEL *match_failed, LABEL *type_error, int deconstructed_pos);
+
static int
iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *matched, LABEL *unmatched, int in_alt_pattern, int deconstructed_pos)
{
@@ -5701,52 +5703,8 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
ADD_INSNL(ret, line, branchunless, match_failed);
}
- // NOTE: this optimization allows us to re-use the #deconstruct value
- // (or its absence).
- // `deconstructed_pos` contains the distance to the stack relative location
- // where the value is stored.
- if (deconstructed_pos) {
- // If value is nil then we haven't tried to deconstruct
- ADD_INSN1(ret, line, topn, INT2FIX(deconstructed_pos));
- ADD_INSNL(ret, line, branchnil, deconstruct);
-
- // If false then the value is not deconstructable
- ADD_INSN1(ret, line, topn, INT2FIX(deconstructed_pos));
- ADD_INSNL(ret, line, branchunless, match_failed);
+ CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, deconstructed_pos));
- // Drop value, add deconstructed to the stack and jump
- ADD_INSN(ret, line, pop);
- ADD_INSN1(ret, line, topn, INT2FIX(deconstructed_pos - 1));
- ADD_INSNL(ret, line, jump, deconstructed);
- } else {
- ADD_INSNL(ret, line, jump, deconstruct);
- }
-
- ADD_LABEL(ret, deconstruct);
- ADD_INSN(ret, line, dup);
- ADD_INSN1(ret, line, putobject, ID2SYM(rb_intern("deconstruct")));
- ADD_SEND(ret, line, idRespond_to, INT2FIX(1));
-
- // Cache the result of respond_to? (in case it's false is stays there, if true — it's overwritten after #deconstruct)
- if (deconstructed_pos) {
- ADD_INSN1(ret, line, setn, INT2FIX(deconstructed_pos + 1));
- }
-
- ADD_INSNL(ret, line, branchunless, match_failed);
-
- ADD_SEND(ret, line, rb_intern("deconstruct"), INT2FIX(0));
-
- // Cache the result (if it's cacheable — currently, only top-level array patterns)
- if (deconstructed_pos) {
- ADD_INSN1(ret, line, setn, INT2FIX(deconstructed_pos));
- }
-
- ADD_INSN(ret, line, dup);
- ADD_INSN1(ret, line, checktype, INT2FIX(T_ARRAY));
- ADD_INSNL(ret, line, branchunless, type_error);
- ADD_INSNL(ret, line, jump, deconstructed);
-
- ADD_LABEL(ret, deconstructed);
ADD_INSN(ret, line, dup);
ADD_SEND(ret, line, idLength, INT2FIX(0));
ADD_INSN1(ret, line, putobject, INT2FIX(min_argc));
@@ -5873,9 +5831,11 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
const NODE *args = fpinfo->args;
const int args_num = fpinfo->args ? rb_long2int(fpinfo->args->nd_alen) : 0;
- LABEL *match_failed, *type_error;
+ LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
match_failed = NEW_LABEL(line);
type_error = NEW_LABEL(line);
+ deconstruct = NEW_LABEL(line);
+ deconstructed = NEW_LABEL(line);
if (node->nd_pconst) {
ADD_INSN(ret, line, dup);
@@ -5884,16 +5844,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
ADD_INSNL(ret, line, branchunless, match_failed);
}
- ADD_INSN(ret, line, dup);
- ADD_INSN1(ret, line, putobject, ID2SYM(rb_intern("deconstruct")));
- ADD_SEND(ret, line, idRespond_to, INT2FIX(1));
- ADD_INSNL(ret, line, branchunless, match_failed);
-
- ADD_SEND(ret, line, rb_intern("deconstruct"), INT2FIX(0));
-
- ADD_INSN(ret, line, dup);
- ADD_INSN1(ret, line, checktype, INT2FIX(T_ARRAY));
- ADD_INSNL(ret, line, branchunless, type_error);
+ CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, deconstructed_pos));
ADD_INSN(ret, line, dup);
ADD_SEND(ret, line, idLength, INT2FIX(0));
@@ -5933,7 +5884,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
}
ADD_SEND(ret, line, idAREF, INT2FIX(1));
- CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, next_loop, in_alt_pattern));
+ CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, next_loop, in_alt_pattern, FALSE));
args = args->nd_next;
}
@@ -5942,7 +5893,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
ADD_INSN1(ret, line, putobject, INT2FIX(0));
ADD_INSN1(ret, line, topn, INT2FIX(2));
ADD_SEND(ret, line, idAREF, INT2FIX(2));
- CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->pre_rest_arg, find_failed, in_alt_pattern));
+ CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->pre_rest_arg, find_failed, in_alt_pattern, FALSE));
}
if (NODE_NAMED_REST_P(fpinfo->post_rest_arg)) {
ADD_INSN1(ret, line, topn, INT2FIX(3));
@@ -5951,7 +5902,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
ADD_SEND(ret, line, idPLUS, INT2FIX(1));
ADD_INSN1(ret, line, topn, INT2FIX(3));
ADD_SEND(ret, line, idAREF, INT2FIX(2));
- CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->post_rest_arg, find_failed, in_alt_pattern));
+ CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->post_rest_arg, find_failed, in_alt_pattern, FALSE));
}
ADD_INSNL(ret, line, jump, find_succeeded);
@@ -6289,6 +6240,61 @@ iseq_compile_pattern_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *
}
static int
+iseq_compile_array_deconstruct(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *deconstruct, LABEL *deconstructed, LABEL *match_failed, LABEL *type_error, int deconstructed_pos)
+{
+ const int line = nd_line(node);
+
+ // NOTE: this optimization allows us to re-use the #deconstruct value
+ // (or its absence).
+ // `deconstructed_pos` contains the distance to the stack relative location
+ // where the value is stored.
+ if (deconstructed_pos) {
+ // If value is nil then we haven't tried to deconstruct
+ ADD_INSN1(ret, line, topn, INT2FIX(deconstructed_pos));
+ ADD_INSNL(ret, line, branchnil, deconstruct);
+
+ // If false then the value is not deconstructable
+ ADD_INSN1(ret, line, topn, INT2FIX(deconstructed_pos));
+ ADD_INSNL(ret, line, branchunless, match_failed);
+
+ // Drop value, add deconstructed to the stack and jump
+ ADD_INSN(ret, line, pop);
+ ADD_INSN1(ret, line, topn, INT2FIX(deconstructed_pos - 1));
+ ADD_INSNL(ret, line, jump, deconstructed);
+ } else {
+ ADD_INSNL(ret, line, jump, deconstruct);
+ }
+
+ ADD_LABEL(ret, deconstruct);
+ ADD_INSN(ret, line, dup);
+ ADD_INSN1(ret, line, putobject, ID2SYM(rb_intern("deconstruct")));
+ ADD_SEND(ret, line, idRespond_to, INT2FIX(1));
+
+ // Cache the result of respond_to? (in case it's false is stays there, if true — it's overwritten after #deconstruct)
+ if (deconstructed_pos) {
+ ADD_INSN1(ret, line, setn, INT2FIX(deconstructed_pos + 1));
+ }
+
+ ADD_INSNL(ret, line, branchunless, match_failed);
+
+ ADD_SEND(ret, line, rb_intern("deconstruct"), INT2FIX(0));
+
+ // Cache the result (if it's cacheable — currently, only top-level array patterns)
+ if (deconstructed_pos) {
+ ADD_INSN1(ret, line, setn, INT2FIX(deconstructed_pos));
+ }
+
+ ADD_INSN(ret, line, dup);
+ ADD_INSN1(ret, line, checktype, INT2FIX(T_ARRAY));
+ ADD_INSNL(ret, line, branchunless, type_error);
+ ADD_INSNL(ret, line, jump, deconstructed);
+
+ ADD_LABEL(ret, deconstructed);
+
+ return COMPILE_OK;
+}
+
+static int
compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
{
const NODE *pattern;
diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb
index 78e3a4e8f3..8c529851b2 100644
--- a/test/ruby/test_pattern_matching.rb
+++ b/test/ruby/test_pattern_matching.rb
@@ -1340,6 +1340,15 @@ END
true
end
end
+
+ assert_block do
+ case CDeconstructCache.new([[0, :a, 1]])
+ in [*, String => x, *]
+ false
+ in [*, Symbol => x, *]
+ true
+ end
+ end
end
################################################################