@@ -336,14 +336,20 @@ def box(self, src: Value) -> Value:
336336 return src
337337
338338 def unbox_or_cast (
339- self , src : Value , target_type : RType , line : int , * , can_borrow : bool = False
339+ self ,
340+ src : Value ,
341+ target_type : RType ,
342+ line : int ,
343+ * ,
344+ can_borrow : bool = False ,
345+ unchecked : bool = False ,
340346 ) -> Value :
341347 if target_type .is_unboxed :
342348 return self .add (Unbox (src , target_type , line ))
343349 else :
344350 if can_borrow :
345351 self .keep_alives .append (src )
346- return self .add (Cast (src , target_type , line , borrow = can_borrow ))
352+ return self .add (Cast (src , target_type , line , borrow = can_borrow , unchecked = unchecked ))
347353
348354 def coerce (
349355 self ,
@@ -2514,6 +2520,22 @@ def translate_eq_cmp(self, lreg: Value, rreg: Value, expr_op: str, line: int) ->
25142520 if is_bytes_rprimitive (ltype ) and is_bytes_rprimitive (rtype ):
25152521 return self .compare_bytes (lreg , rreg , expr_op , line )
25162522
2523+ lopt = optional_value_type (ltype )
2524+ ropt = optional_value_type (rtype )
2525+
2526+ # Can we do a quick comparison of two optional types (special case None values)?
2527+ fast_opt_eq = False
2528+ if lopt is not None :
2529+ if ropt is not None and is_same_type (lopt , ropt ) and self ._never_equal_to_none (lopt ):
2530+ fast_opt_eq = True
2531+ if is_same_type (lopt , rtype ) and self ._never_equal_to_none (lopt ):
2532+ fast_opt_eq = True
2533+ elif ropt is not None :
2534+ if is_same_type (ropt , ltype ) and self ._never_equal_to_none (ropt ):
2535+ fast_opt_eq = True
2536+ if fast_opt_eq :
2537+ return self ._translate_fast_optional_eq_cmp (lreg , rreg , expr_op , line )
2538+
25172539 if not (isinstance (ltype , RInstance ) and ltype == rtype ):
25182540 return None
25192541
@@ -2540,6 +2562,76 @@ def translate_eq_cmp(self, lreg: Value, rreg: Value, expr_op: str, line: int) ->
25402562
25412563 return self .gen_method_call (lreg , op_methods [expr_op ], [rreg ], ltype , line )
25422564
2565+ def _never_equal_to_none (self , typ : RType ) -> bool :
2566+ """Are the values of type never equal to None?"""
2567+ # TODO: Support RInstance with no custom __eq__/__ne__ and other primitive types.
2568+ return is_str_rprimitive (typ ) or is_bytes_rprimitive (typ )
2569+
2570+ def _translate_fast_optional_eq_cmp (
2571+ self , lreg : Value , rreg : Value , expr_op : str , line : int
2572+ ) -> Value :
2573+ """Generate eq/ne fast path between 'X | None' and ('X | None' or X).
2574+
2575+ Assume 'X' never compares equal to None.
2576+ """
2577+ if not isinstance (lreg .type , RUnion ):
2578+ lreg , rreg = rreg , lreg
2579+ value_typ = optional_value_type (lreg .type )
2580+ assert value_typ
2581+ res = Register (bool_rprimitive )
2582+
2583+ # Fast path: left value is None?
2584+ cmp = self .add (ComparisonOp (lreg , self .none_object (), ComparisonOp .EQ , line ))
2585+ l_none = BasicBlock ()
2586+ l_not_none = BasicBlock ()
2587+ out = BasicBlock ()
2588+ self .add (Branch (cmp , l_none , l_not_none , Branch .BOOL ))
2589+ self .activate_block (l_none )
2590+ if not isinstance (rreg .type , RUnion ):
2591+ val = self .false () if expr_op == "==" else self .true ()
2592+ self .add (Assign (res , val ))
2593+ else :
2594+ op = ComparisonOp .EQ if expr_op == "==" else ComparisonOp .NEQ
2595+ cmp = self .add (ComparisonOp (rreg , self .none_object (), op , line ))
2596+ self .add (Assign (res , cmp ))
2597+ self .goto (out )
2598+
2599+ self .activate_block (l_not_none )
2600+ if not isinstance (rreg .type , RUnion ):
2601+ # Both operands are known to be not None, perform specialized comparison
2602+ eq = self .translate_eq_cmp (
2603+ self .unbox_or_cast (lreg , value_typ , line , can_borrow = True , unchecked = True ),
2604+ rreg ,
2605+ expr_op ,
2606+ line ,
2607+ )
2608+ assert eq is not None
2609+ self .add (Assign (res , eq ))
2610+ else :
2611+ r_none = BasicBlock ()
2612+ r_not_none = BasicBlock ()
2613+ # Fast path: right value is None?
2614+ cmp = self .add (ComparisonOp (rreg , self .none_object (), ComparisonOp .EQ , line ))
2615+ self .add (Branch (cmp , r_none , r_not_none , Branch .BOOL ))
2616+ self .activate_block (r_none )
2617+ # None vs not-None
2618+ val = self .false () if expr_op == "==" else self .true ()
2619+ self .add (Assign (res , val ))
2620+ self .goto (out )
2621+ self .activate_block (r_not_none )
2622+ # Both operands are known to be not None, perform specialized comparison
2623+ eq = self .translate_eq_cmp (
2624+ self .unbox_or_cast (lreg , value_typ , line , can_borrow = True , unchecked = True ),
2625+ self .unbox_or_cast (rreg , value_typ , line , can_borrow = True , unchecked = True ),
2626+ expr_op ,
2627+ line ,
2628+ )
2629+ assert eq is not None
2630+ self .add (Assign (res , eq ))
2631+ self .goto (out )
2632+ self .activate_block (out )
2633+ return res
2634+
25432635 def translate_is_op (self , lreg : Value , rreg : Value , expr_op : str , line : int ) -> Value :
25442636 """Create equality comparison operation between object identities
25452637
0 commit comments