@@ -10,28 +10,41 @@ pub enum PrimVal {
1010 U8 ( u8 ) , U16 ( u16 ) , U32 ( u32 ) , U64 ( u64 ) ,
1111
1212 AbstractPtr ( Pointer ) ,
13+ FnPtr ( Pointer ) ,
1314 IntegerPtr ( u64 ) ,
1415}
1516
16- pub fn binary_op < ' tcx > ( bin_op : mir:: BinOp , left : PrimVal , right : PrimVal ) -> EvalResult < ' tcx , PrimVal > {
17+ /// returns the result of the operation and whether the operation overflowed
18+ pub fn binary_op < ' tcx > ( bin_op : mir:: BinOp , left : PrimVal , right : PrimVal ) -> EvalResult < ' tcx , ( PrimVal , bool ) > {
1719 use rustc:: mir:: repr:: BinOp :: * ;
1820 use self :: PrimVal :: * ;
1921
22+ macro_rules! overflow {
23+ ( $v: ident, $v2: ident, $l: ident, $op: ident, $r: ident) => ( {
24+ let ( val, of) = $l. $op( $r) ;
25+ if of {
26+ return Ok ( ( $v( val) , true ) ) ;
27+ } else {
28+ $v( val)
29+ }
30+ } )
31+ }
32+
2033 macro_rules! int_binops {
2134 ( $v: ident, $l: ident, $r: ident) => ( {
2235 match bin_op {
23- Add => $v ( $l + $r) ,
24- Sub => $v ( $l - $r) ,
25- Mul => $v ( $l * $r) ,
26- Div => $v ( $l / $r) ,
27- Rem => $v ( $l % $r) ,
36+ Add => overflow! ( $v , $v , $l , overflowing_add , $r) ,
37+ Sub => overflow! ( $v , $v , $l , overflowing_sub , $r) ,
38+ Mul => overflow! ( $v , $v , $l , overflowing_mul , $r) ,
39+ Div => overflow! ( $v , $v , $l , overflowing_div , $r) ,
40+ Rem => overflow! ( $v , $v , $l , overflowing_rem , $r) ,
2841 BitXor => $v( $l ^ $r) ,
2942 BitAnd => $v( $l & $r) ,
3043 BitOr => $v( $l | $r) ,
3144
32- // TODO(solson): Can have differently-typed RHS.
33- Shl => $v ( $l << $r ) ,
34- Shr => $v ( $l >> $r ) ,
45+ // these have already been handled
46+ Shl => unreachable! ( ) ,
47+ Shr => unreachable! ( ) ,
3548
3649 Eq => Bool ( $l == $r) ,
3750 Ne => Bool ( $l != $r) ,
@@ -53,6 +66,58 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva
5366 }
5467 }
5568
69+ match bin_op {
70+ // can have rhs with a different numeric type
71+ Shl | Shr => {
72+ // these numbers are the maximum number a bitshift rhs could possibly have
73+ // e.g. u16 can be bitshifted by 0..16, so masking with 0b1111 (16 - 1) will ensure we are in that range
74+ let type_bits: u32 = match left {
75+ I8 ( _) | U8 ( _) => 8 ,
76+ I16 ( _) | U16 ( _) => 16 ,
77+ I32 ( _) | U32 ( _) => 32 ,
78+ I64 ( _) | U64 ( _) => 64 ,
79+ _ => unreachable ! ( ) ,
80+ } ;
81+ assert ! ( type_bits. is_power_of_two( ) ) ;
82+ // turn into `u32` because `overflowing_sh{l,r}` only take `u32`
83+ let r = match right {
84+ I8 ( i) => i as u32 ,
85+ I16 ( i) => i as u32 ,
86+ I32 ( i) => i as u32 ,
87+ I64 ( i) => i as u32 ,
88+ U8 ( i) => i as u32 ,
89+ U16 ( i) => i as u32 ,
90+ U32 ( i) => i as u32 ,
91+ U64 ( i) => i as u32 ,
92+ _ => panic ! ( "bad MIR: bitshift rhs is not integral" ) ,
93+ } ;
94+ // apply mask
95+ let r = r & ( type_bits - 1 ) ;
96+ macro_rules! shift {
97+ ( $v: ident, $l: ident, $r: ident) => ( {
98+ match bin_op {
99+ Shl => overflow!( $v, U32 , $l, overflowing_shl, $r) ,
100+ Shr => overflow!( $v, U32 , $l, overflowing_shr, $r) ,
101+ _ => unreachable!( ) ,
102+ }
103+ } )
104+ }
105+ let val = match left {
106+ I8 ( l) => shift ! ( I8 , l, r) ,
107+ I16 ( l) => shift ! ( I16 , l, r) ,
108+ I32 ( l) => shift ! ( I32 , l, r) ,
109+ I64 ( l) => shift ! ( I64 , l, r) ,
110+ U8 ( l) => shift ! ( U8 , l, r) ,
111+ U16 ( l) => shift ! ( U16 , l, r) ,
112+ U32 ( l) => shift ! ( U32 , l, r) ,
113+ U64 ( l) => shift ! ( U64 , l, r) ,
114+ _ => unreachable ! ( ) ,
115+ } ;
116+ return Ok ( ( val, false ) ) ;
117+ } ,
118+ _ => { } ,
119+ }
120+
56121 let val = match ( left, right) {
57122 ( I8 ( l) , I8 ( r) ) => int_binops ! ( I8 , l, r) ,
58123 ( I16 ( l) , I16 ( r) ) => int_binops ! ( I16 , l, r) ,
@@ -80,12 +145,23 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva
80145
81146 ( IntegerPtr ( l) , IntegerPtr ( r) ) => int_binops ! ( IntegerPtr , l, r) ,
82147
83- ( AbstractPtr ( _) , IntegerPtr ( _) ) | ( IntegerPtr ( _) , AbstractPtr ( _) ) =>
84- return unrelated_ptr_ops ( bin_op) ,
148+ ( AbstractPtr ( _) , IntegerPtr ( _) ) |
149+ ( IntegerPtr ( _) , AbstractPtr ( _) ) |
150+ ( FnPtr ( _) , AbstractPtr ( _) ) |
151+ ( AbstractPtr ( _) , FnPtr ( _) ) |
152+ ( FnPtr ( _) , IntegerPtr ( _) ) |
153+ ( IntegerPtr ( _) , FnPtr ( _) ) =>
154+ unrelated_ptr_ops ( bin_op) ?,
155+
156+ ( FnPtr ( l_ptr) , FnPtr ( r_ptr) ) => match bin_op {
157+ Eq => Bool ( l_ptr == r_ptr) ,
158+ Ne => Bool ( l_ptr != r_ptr) ,
159+ _ => return Err ( EvalError :: Unimplemented ( format ! ( "unimplemented fn ptr comparison: {:?}" , bin_op) ) ) ,
160+ } ,
85161
86162 ( AbstractPtr ( l_ptr) , AbstractPtr ( r_ptr) ) => {
87163 if l_ptr. alloc_id != r_ptr. alloc_id {
88- return unrelated_ptr_ops ( bin_op) ;
164+ return Ok ( ( unrelated_ptr_ops ( bin_op) ? , false ) ) ;
89165 }
90166
91167 let l = l_ptr. offset ;
@@ -105,7 +181,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva
105181 ( l, r) => return Err ( EvalError :: Unimplemented ( format ! ( "unimplemented binary op: {:?}, {:?}, {:?}" , l, r, bin_op) ) ) ,
106182 } ;
107183
108- Ok ( val)
184+ Ok ( ( val, false ) )
109185}
110186
111187pub fn unary_op < ' tcx > ( un_op : mir:: UnOp , val : PrimVal ) -> EvalResult < ' tcx , PrimVal > {
0 commit comments