Skip to content

Commit fa5326f

Browse files
committed
dedicated handling for binops on bool and char (UB if they are not valid)
1 parent a5a751d commit fa5326f

File tree

2 files changed

+113
-60
lines changed

2 files changed

+113
-60
lines changed

src/librustc/mir/interpret/value.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,43 +187,51 @@ impl<'tcx> Scalar {
187187
}
188188
}
189189

190-
fn to_u8(self) -> EvalResult<'static, u8> {
190+
pub fn to_char(self) -> EvalResult<'tcx, char> {
191+
let val = self.to_u32()?;
192+
match ::std::char::from_u32(val) {
193+
Some(c) => Ok(c),
194+
None => err!(InvalidChar(val as u128)),
195+
}
196+
}
197+
198+
pub fn to_u8(self) -> EvalResult<'static, u8> {
191199
let sz = Size::from_bits(8);
192200
let b = self.to_bits(sz)?;
193201
assert_eq!(b as u8 as u128, b);
194202
Ok(b as u8)
195203
}
196204

197-
fn to_u32(self) -> EvalResult<'static, u32> {
205+
pub fn to_u32(self) -> EvalResult<'static, u32> {
198206
let sz = Size::from_bits(32);
199207
let b = self.to_bits(sz)?;
200208
assert_eq!(b as u32 as u128, b);
201209
Ok(b as u32)
202210
}
203211

204-
fn to_usize(self, cx: impl HasDataLayout) -> EvalResult<'static, u64> {
212+
pub fn to_usize(self, cx: impl HasDataLayout) -> EvalResult<'static, u64> {
205213
let b = self.to_bits(cx.data_layout().pointer_size)?;
206214
assert_eq!(b as u64 as u128, b);
207215
Ok(b as u64)
208216
}
209217

210-
fn to_i8(self) -> EvalResult<'static, i8> {
218+
pub fn to_i8(self) -> EvalResult<'static, i8> {
211219
let sz = Size::from_bits(8);
212220
let b = self.to_bits(sz)?;
213221
let b = sign_extend(b, sz) as i128;
214222
assert_eq!(b as i8 as i128, b);
215223
Ok(b as i8)
216224
}
217225

218-
fn to_i32(self) -> EvalResult<'static, i32> {
226+
pub fn to_i32(self) -> EvalResult<'static, i32> {
219227
let sz = Size::from_bits(32);
220228
let b = self.to_bits(sz)?;
221229
let b = sign_extend(b, sz) as i128;
222230
assert_eq!(b as i32 as i128, b);
223231
Ok(b as i32)
224232
}
225233

226-
fn to_isize(self, cx: impl HasDataLayout) -> EvalResult<'static, i64> {
234+
pub fn to_isize(self, cx: impl HasDataLayout) -> EvalResult<'static, i64> {
227235
let b = self.to_bits(cx.data_layout().pointer_size)?;
228236
let b = sign_extend(b, cx.data_layout().pointer_size) as i128;
229237
assert_eq!(b as i64 as i128, b);
@@ -295,6 +303,11 @@ impl<'tcx> ScalarMaybeUndef {
295303
self.not_undef()?.to_bool()
296304
}
297305

306+
#[inline(always)]
307+
pub fn to_char(self) -> EvalResult<'tcx, char> {
308+
self.not_undef()?.to_char()
309+
}
310+
298311
#[inline(always)]
299312
pub fn to_u8(self) -> EvalResult<'tcx, u8> {
300313
self.not_undef()?.to_u8()

src/librustc_mir/interpret/operator.rs

Lines changed: 94 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use rustc::mir;
12-
use rustc::ty::{self, layout::{self, TyLayout}};
12+
use rustc::ty::{self, layout::TyLayout};
1313
use syntax::ast::FloatTy;
1414
use rustc_apfloat::ieee::{Double, Single};
1515
use rustc_apfloat::Float;
@@ -60,32 +60,102 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
6060
let left = left.to_scalar()?;
6161
let right = right.to_scalar()?;
6262

63-
let left_kind = match left_layout.abi {
64-
layout::Abi::Scalar(ref scalar) => scalar.value,
65-
_ => return err!(TypeNotPrimitive(left_layout.ty)),
66-
};
67-
let right_kind = match right_layout.abi {
68-
layout::Abi::Scalar(ref scalar) => scalar.value,
69-
_ => return err!(TypeNotPrimitive(right_layout.ty)),
70-
};
7163
trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})",
72-
bin_op, left, left_kind, right, right_kind);
64+
bin_op, left, left_layout.ty.sty, right, right_layout.ty.sty);
7365

74-
// I: Handle operations that support pointers
75-
if !left_kind.is_float() && !right_kind.is_float() {
76-
if let Some(handled) =
77-
M::try_ptr_op(self, bin_op, left, left_layout, right, right_layout)?
78-
{
79-
return Ok(handled);
66+
// Handle non-integer operations
67+
if let ty::Char = left_layout.ty.sty {
68+
assert_eq!(right_layout.ty.sty, ty::Char);
69+
let l = left.to_char()?;
70+
let r = right.to_char()?;
71+
let res = match bin_op {
72+
Eq => l == r,
73+
Ne => l != r,
74+
Lt => l < r,
75+
Le => l <= r,
76+
Gt => l > r,
77+
Ge => l >= r,
78+
_ => bug!("Invalid operation on char: {:?}", bin_op),
79+
};
80+
return Ok((Scalar::from_bool(res), false));
81+
}
82+
if let ty::Bool = left_layout.ty.sty {
83+
assert_eq!(right_layout.ty.sty, ty::Bool);
84+
let l = left.to_bool()?;
85+
let r = right.to_bool()?;
86+
let res = match bin_op {
87+
Eq => l == r,
88+
Ne => l != r,
89+
Lt => l < r,
90+
Le => l <= r,
91+
Gt => l > r,
92+
Ge => l >= r,
93+
BitAnd => l & r,
94+
BitOr => l | r,
95+
BitXor => l ^ r,
96+
_ => bug!("Invalid operation on bool: {:?}", bin_op),
97+
};
98+
return Ok((Scalar::from_bool(res), false));
99+
}
100+
if let ty::Float(fty) = left_layout.ty.sty {
101+
let l = left.to_bits(left_layout.size)?;
102+
let r = right.to_bits(right_layout.size)?;
103+
assert_eq!(right_layout.ty.sty, ty::Float(fty));
104+
macro_rules! float_math {
105+
($ty:path, $size:expr) => {{
106+
let l = <$ty>::from_bits(l);
107+
let r = <$ty>::from_bits(r);
108+
let bitify = |res: ::rustc_apfloat::StatusAnd<$ty>| Scalar::Bits {
109+
bits: res.value.to_bits(),
110+
size: $size,
111+
};
112+
let val = match bin_op {
113+
Eq => Scalar::from_bool(l == r),
114+
Ne => Scalar::from_bool(l != r),
115+
Lt => Scalar::from_bool(l < r),
116+
Le => Scalar::from_bool(l <= r),
117+
Gt => Scalar::from_bool(l > r),
118+
Ge => Scalar::from_bool(l >= r),
119+
Add => bitify(l + r),
120+
Sub => bitify(l - r),
121+
Mul => bitify(l * r),
122+
Div => bitify(l / r),
123+
Rem => bitify(l % r),
124+
_ => bug!("invalid float op: `{:?}`", bin_op),
125+
};
126+
return Ok((val, false));
127+
}};
128+
}
129+
match fty {
130+
FloatTy::F32 => float_math!(Single, 4),
131+
FloatTy::F64 => float_math!(Double, 8),
80132
}
81133
}
134+
// Only integers left
135+
#[inline]
136+
fn is_ptr<'tcx>(ty: ty::Ty<'tcx>) -> bool {
137+
match ty.sty {
138+
ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true,
139+
_ => false,
140+
}
141+
}
142+
assert!(left_layout.ty.is_integral() || is_ptr(left_layout.ty));
143+
assert!(right_layout.ty.is_integral() || is_ptr(right_layout.ty));
144+
145+
// Handle operations that support pointers
146+
if let Some(handled) =
147+
M::try_ptr_op(self, bin_op, left, left_layout, right, right_layout)?
148+
{
149+
return Ok(handled);
150+
}
82151

83-
// II: From now on, everything must be bytes, no pointers
152+
// From now on, everything must be bytes, no pointer values
153+
// (this is independent of the type)
84154
let l = left.to_bits(left_layout.size)?;
85155
let r = right.to_bits(right_layout.size)?;
86156

87-
// These ops can have an RHS with a different numeric type.
88-
if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) {
157+
// Shift ops can have an RHS with a different numeric type.
158+
if bin_op == Shl || bin_op == Shr {
89159
let signed = left_layout.abi.is_signed();
90160
let mut oflo = (r as u32 as u128) != r;
91161
let mut r = r as u32;
@@ -116,18 +186,20 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
116186
}, oflo));
117187
}
118188

119-
if left_kind != right_kind {
189+
// For the remaining ops, the types must be the same on both sides
190+
if left_layout.ty != right_layout.ty {
120191
let msg = format!(
121192
"unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})",
122193
bin_op,
123194
left,
124-
left_kind,
195+
left_layout.ty,
125196
right,
126-
right_kind
197+
right_layout.ty
127198
);
128199
return err!(Unimplemented(msg));
129200
}
130201

202+
// Operations that need special treatment for signed integers
131203
if left_layout.abi.is_signed() {
132204
let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
133205
Lt => Some(i128::lt),
@@ -180,38 +252,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
180252
}
181253
}
182254

183-
if let ty::Float(fty) = left_layout.ty.sty {
184-
macro_rules! float_math {
185-
($ty:path, $size:expr) => {{
186-
let l = <$ty>::from_bits(l);
187-
let r = <$ty>::from_bits(r);
188-
let bitify = |res: ::rustc_apfloat::StatusAnd<$ty>| Scalar::Bits {
189-
bits: res.value.to_bits(),
190-
size: $size,
191-
};
192-
let val = match bin_op {
193-
Eq => Scalar::from_bool(l == r),
194-
Ne => Scalar::from_bool(l != r),
195-
Lt => Scalar::from_bool(l < r),
196-
Le => Scalar::from_bool(l <= r),
197-
Gt => Scalar::from_bool(l > r),
198-
Ge => Scalar::from_bool(l >= r),
199-
Add => bitify(l + r),
200-
Sub => bitify(l - r),
201-
Mul => bitify(l * r),
202-
Div => bitify(l / r),
203-
Rem => bitify(l % r),
204-
_ => bug!("invalid float op: `{:?}`", bin_op),
205-
};
206-
return Ok((val, false));
207-
}};
208-
}
209-
match fty {
210-
FloatTy::F32 => float_math!(Single, 4),
211-
FloatTy::F64 => float_math!(Double, 8),
212-
}
213-
}
214-
215255
let size = left_layout.size.bytes() as u8;
216256

217257
// only ints left

0 commit comments

Comments
 (0)