@@ -608,3 +608,62 @@ def pattern_over_operand(a)
608608 end
609609 assert_equal :match, matched
610610end
611+
612+ assert('keyword pairs after a double splat or a flush go into one hash') do
613+ # The pairs left over once a `**` or a register flush had started the hash
614+ # were given a hash of their own when they outnumbered the operand, and the
615+ # callee read the first one: the pairs were dropped, and the block slot the
616+ # call had counted on held the second hash instead of the block.
617+ def codegen_kw(**k, &b) [k, b && b.call(:o)] end
618+ h = {z: 0}
619+ k, r = codegen_kw(**h, a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8,
620+ i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15) { :blk }
621+ assert_equal 16, k.size
622+ assert_equal 15, k[:o]
623+ assert_equal :blk, r
624+
625+ class CodegenKwParent
626+ def m(**k) k end
627+ end
628+ class CodegenKwChild < CodegenKwParent
629+ def m(h)
630+ super(**h, a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8,
631+ i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15)
632+ end
633+ end
634+ assert_equal 16, CodegenKwChild.new.m(h).size
635+
636+ def codegen_kw_yield(h)
637+ yield(**h, a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8,
638+ i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15)
639+ end
640+ assert_equal 16, codegen_kw_yield(h) { |**k| k }.size
641+
642+ # no `**`: the flush at the register limit starts the hash
643+ k, = codegen_kw(k0: 0, k1: 1, k2: 2, k3: 3, k4: 4, k5: 5, k6: 6, k7: 7, k8: 8,
644+ k9: 9, k10: 10, k11: 11, k12: 12, k13: 13, k14: 14, k15: 15, k16: 16, k17: 17,
645+ k18: 18, k19: 19, k20: 20, k21: 21, k22: 22, k23: 23, k24: 24, k25: 25, k26: 26,
646+ k27: 27, k28: 28, k29: 29, k30: 30, k31: 31, k32: 32, k33: 33, k34: 34, k35: 35,
647+ k36: 36, k37: 37, k38: 38, k39: 39, k40: 40, k41: 41, k42: 42, k43: 43, k44: 44,
648+ k45: 45, k46: 46, k47: 47, k48: 48, k49: 49, k50: 50, k51: 51, k52: 52, k53: 53,
649+ k54: 54, k55: 55, k56: 56, k57: 57, k58: 58, k59: 59, k60: 60, k61: 61, k62: 62)
650+ assert_equal 63, k.size
651+ assert_equal 62, k[:k62]
652+
653+ # a literal in a frame 64 registers deep, where nothing flushes: the second
654+ # hash shifted the gathered arguments by one, and the callee took its first
655+ # argument for the array holding them
656+ def codegen_kw_last(*a) a.last end
657+ k = codegen_kw_last(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
658+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
659+ {**h, k0: 0, k1: 1, k2: 2, k3: 3, k4: 4, k5: 5, k6: 6, k7: 7, k8: 8,
660+ k9: 9, k10: 10, k11: 11, k12: 12, k13: 13, k14: 14, k15: 15, k16: 16, k17: 17,
661+ k18: 18, k19: 19, k20: 20, k21: 21, k22: 22, k23: 23, k24: 24, k25: 25, k26: 26,
662+ k27: 27, k28: 28, k29: 29, k30: 30, k31: 31, k32: 32, k33: 33, k34: 34, k35: 35,
663+ k36: 36, k37: 37, k38: 38, k39: 39, k40: 40, k41: 41, k42: 42, k43: 43, k44: 44,
664+ k45: 45, k46: 46, k47: 47, k48: 48, k49: 49, k50: 50, k51: 51, k52: 52, k53: 53,
665+ k54: 54, k55: 55, k56: 56, k57: 57, k58: 58, k59: 59, k60: 60, k61: 61, k62: 62,
666+ k63: 63, k64: 64})
667+ assert_equal 66, k.size
668+ assert_equal 64, k[:k64]
669+ end
0 commit comments