@@ -498,6 +498,7 @@ pub enum Insn {
498498
499499 // Distinct from `SendWithoutBlock` with `mid:to_s` because does not have a patch point for String to_s being redefined
500500 ObjToString { val : InsnId , call_info : CallInfo , cd : * const rb_call_data , state : InsnId } ,
501+ AnyToString { val : InsnId , str : InsnId , state : InsnId } ,
501502
502503 /// Side-exit if val doesn't have the expected type.
503504 GuardType { val : InsnId , guard_type : Type , state : InsnId } ,
@@ -699,6 +700,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
699700 Insn :: ArrayExtend { left, right, .. } => write ! ( f, "ArrayExtend {left}, {right}" ) ,
700701 Insn :: ArrayPush { array, val, .. } => write ! ( f, "ArrayPush {array}, {val}" ) ,
701702 Insn :: ObjToString { val, .. } => { write ! ( f, "ObjToString {val}" ) } ,
703+ Insn :: AnyToString { val, str, .. } => { write ! ( f, "AnyToString {val}, str: {str}" ) } ,
702704 Insn :: SideExit { .. } => write ! ( f, "SideExit" ) ,
703705 Insn :: PutSpecialObject { value_type } => {
704706 write ! ( f, "PutSpecialObject {}" , value_type)
@@ -1023,6 +1025,11 @@ impl Function {
10231025 cd : * cd,
10241026 state : * state,
10251027 } ,
1028+ AnyToString { val, str, state } => AnyToString {
1029+ val : find ! ( * val) ,
1030+ str : find ! ( * str ) ,
1031+ state : * state,
1032+ } ,
10261033 SendWithoutBlock { self_val, call_info, cd, args, state } => SendWithoutBlock {
10271034 self_val : find ! ( * self_val) ,
10281035 call_info : call_info. clone ( ) ,
@@ -1154,6 +1161,7 @@ impl Function {
11541161 Insn :: ToNewArray { .. } => types:: ArrayExact ,
11551162 Insn :: ToArray { .. } => types:: ArrayExact ,
11561163 Insn :: ObjToString { .. } => types:: BasicObject ,
1164+ Insn :: AnyToString { .. } => types:: String ,
11571165 }
11581166 }
11591167
@@ -1398,14 +1406,21 @@ impl Function {
13981406 self . make_equal_to ( insn_id, replacement) ;
13991407 }
14001408 Insn :: ObjToString { val, call_info, cd, state, .. } => {
1401- if self . is_a ( val, types:: StringExact ) {
1409+ if self . is_a ( val, types:: String ) {
14021410 // behaves differently from `SendWithoutBlock` with `mid:to_s` because ObjToString should not have a patch point for String to_s being redefined
14031411 self . make_equal_to ( insn_id, val) ;
14041412 } else {
14051413 let replacement = self . push_insn ( block, Insn :: SendWithoutBlock { self_val : val, call_info, cd, args : vec ! [ ] , state } ) ;
14061414 self . make_equal_to ( insn_id, replacement)
14071415 }
14081416 }
1417+ Insn :: AnyToString { str, .. } => {
1418+ if self . is_a ( str, types:: String ) {
1419+ self . make_equal_to ( insn_id, str) ;
1420+ } else {
1421+ self . push_insn_id ( block, insn_id) ;
1422+ }
1423+ }
14091424 _ => { self . push_insn_id ( block, insn_id) ; }
14101425 }
14111426 }
@@ -1782,6 +1797,11 @@ impl Function {
17821797 worklist. push_back ( val) ;
17831798 worklist. push_back ( state) ;
17841799 }
1800+ Insn :: AnyToString { val, str, state, .. } => {
1801+ worklist. push_back ( val) ;
1802+ worklist. push_back ( str) ;
1803+ worklist. push_back ( state) ;
1804+ }
17851805 Insn :: GetGlobal { state, .. } |
17861806 Insn :: SideExit { state } => worklist. push_back ( state) ,
17871807 }
@@ -2783,6 +2803,14 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
27832803 let objtostring = fun. push_insn ( block, Insn :: ObjToString { val : recv, call_info : CallInfo { method_name } , cd, state : exit_id } ) ;
27842804 state. stack_push ( objtostring)
27852805 }
2806+ YARVINSN_anytostring => {
2807+ let str = state. stack_pop ( ) ?;
2808+ let val = state. stack_pop ( ) ?;
2809+
2810+ let exit_id = fun. push_insn ( block, Insn :: Snapshot { state : exit_state } ) ;
2811+ let anytostring = fun. push_insn ( block, Insn :: AnyToString { val, str, state : exit_id } ) ;
2812+ state. stack_push ( anytostring) ;
2813+ }
27862814 _ => {
27872815 // Unknown opcode; side-exit into the interpreter
27882816 let exit_id = fun. push_insn ( block, Insn :: Snapshot { state : exit_state } ) ;
@@ -4452,7 +4480,7 @@ mod tests {
44524480 }
44534481
44544482 #[ test]
4455- fn test_objtostring ( ) {
4483+ fn test_objtostring_anytostring ( ) {
44564484 eval ( "
44574485 def test = \" #{1}\"
44584486 " ) ;
@@ -4462,6 +4490,7 @@ mod tests {
44624490 v2:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
44634491 v3:Fixnum[1] = Const Value(1)
44644492 v5:BasicObject = ObjToString v3
4493+ v7:String = AnyToString v3, str: v5
44654494 SideExit
44664495 "# ] ] ) ;
44674496 }
@@ -6010,7 +6039,7 @@ mod opt_tests {
60106039 }
60116040
60126041 #[ test]
6013- fn test_objtostring_string ( ) {
6042+ fn test_objtostring_anytostring_string ( ) {
60146043 eval ( r##"
60156044 def test = "#{('foo')}"
60166045 "## ) ;
@@ -6025,7 +6054,7 @@ mod opt_tests {
60256054 }
60266055
60276056 #[ test]
6028- fn test_objtostring_with_non_string ( ) {
6057+ fn test_objtostring_anytostring_with_non_string ( ) {
60296058 eval ( r##"
60306059 def test = "#{1}"
60316060 "## ) ;
@@ -6034,7 +6063,8 @@ mod opt_tests {
60346063 bb0(v0:BasicObject):
60356064 v2:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
60366065 v3:Fixnum[1] = Const Value(1)
6037- v8:BasicObject = SendWithoutBlock v3, :to_s
6066+ v10:BasicObject = SendWithoutBlock v3, :to_s
6067+ v7:String = AnyToString v3, str: v10
60386068 SideExit
60396069 "# ] ] ) ;
60406070 }
0 commit comments