Sign extend constants in range patterns

This commit is contained in:
Oliver Schneider 2018-04-13 18:31:15 +02:00 committed by Oliver Schneider
parent 883bf4ba2e
commit b22c9c01fb
No known key found for this signature in database
GPG Key ID: A69F8D225B3AD7D9
5 changed files with 69 additions and 23 deletions

View File

@ -985,15 +985,17 @@ fn slice_pat_covered_by_constructor(tcx: TyCtxt, _span: Span,
Ok(true)
}
fn constructor_covered_by_range(ctor: &Constructor,
from: &ConstVal, to: &ConstVal,
end: RangeEnd,
ty: Ty)
-> Result<bool, ErrorReported> {
fn constructor_covered_by_range<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
ctor: &Constructor,
from: &ConstVal, to: &ConstVal,
end: RangeEnd,
ty: Ty<'tcx>,
) -> Result<bool, ErrorReported> {
trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, from, to, ty);
let cmp_from = |c_from| compare_const_vals(c_from, from, ty)
let cmp_from = |c_from| compare_const_vals(tcx, c_from, from, ty)
.map(|res| res != Ordering::Less);
let cmp_to = |c_to| compare_const_vals(c_to, to, ty);
let cmp_to = |c_to| compare_const_vals(tcx, c_to, to, ty);
macro_rules! some_or_ok {
($e:expr) => {
match $e {
@ -1105,6 +1107,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
},
_ => {
match constructor_covered_by_range(
cx.tcx,
constructor, &value.val, &value.val, RangeEnd::Included,
value.ty,
) {
@ -1118,6 +1121,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
PatternKind::Range { lo, hi, ref end } => {
match constructor_covered_by_range(
cx.tcx,
constructor, &lo.val, &hi.val, end.clone(), lo.ty,
) {
Ok(true) => Some(vec![]),

View File

@ -16,7 +16,7 @@ mod check_match;
pub use self::check_match::check_crate;
pub(crate) use self::check_match::check_match;
use interpret::{const_val_field, const_discr};
use interpret::{const_val_field, const_discr, self};
use rustc::middle::const_val::ConstVal;
use rustc::mir::{Field, BorrowKind, Mutability};
@ -372,7 +372,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
(PatternKind::Constant { value: lo },
PatternKind::Constant { value: hi }) => {
use std::cmp::Ordering;
match (end, compare_const_vals(&lo.val, &hi.val, ty).unwrap()) {
match (end, compare_const_vals(self.tcx, &lo.val, &hi.val, ty).unwrap()) {
(RangeEnd::Excluded, Ordering::Less) =>
PatternKind::Range { lo, hi, end },
(RangeEnd::Excluded, _) => {
@ -1076,7 +1076,12 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> {
}
}
pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option<Ordering> {
pub fn compare_const_vals<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
a: &ConstVal,
b: &ConstVal,
ty: Ty<'tcx>,
) -> Option<Ordering> {
use rustc_const_math::ConstFloat;
trace!("compare_const_vals: {:?}, {:?}", a, b);
use rustc::mir::interpret::{Value, PrimVal};
@ -1096,7 +1101,11 @@ pub fn compare_const_vals(a: &ConstVal, b: &ConstVal, ty: Ty) -> Option<Ordering
// FIXME(oli-obk): report cmp errors?
l.try_cmp(r).ok()
},
ty::TyInt(_) => Some((a as i128).cmp(&(b as i128))),
ty::TyInt(_) => {
let a = interpret::sign_extend(tcx, a, ty).expect("layout error for TyInt");
let b = interpret::sign_extend(tcx, b, ty).expect("layout error for TyInt");
Some((a as i128).cmp(&(b as i128)))
},
_ => Some(a.cmp(&b)),
}
},

View File

@ -1679,21 +1679,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
}
pub fn sign_extend(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
let layout = self.layout_of(ty)?;
let size = layout.size.bits();
assert!(layout.abi.is_signed());
// sign extend
let amt = 128 - size;
// shift the unsigned value to the left
// and back to the right as signed (essentially fills with FF on the left)
Ok((((value << amt) as i128) >> amt) as u128)
super::sign_extend(self.tcx.tcx, value, ty)
}
pub fn truncate(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
let size = self.layout_of(ty)?.size.bits();
let amt = 128 - size;
// truncate (shift left to drop out leftover values, shift right to fill with zeroes)
Ok((value << amt) >> amt)
super::truncate(self.tcx.tcx, value, ty)
}
}

View File

@ -31,3 +31,27 @@ pub use self::const_eval::{
pub use self::machine::Machine;
pub use self::memory::{write_target_uint, write_target_int, read_target_uint};
use rustc::mir::interpret::{EvalResult, EvalErrorKind};
use rustc::ty::{Ty, TyCtxt, ParamEnv};
pub fn sign_extend<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
let param_env = ParamEnv::empty();
let layout = tcx.layout_of(param_env.and(ty)).map_err(|layout| EvalErrorKind::Layout(layout))?;
let size = layout.size.bits();
assert!(layout.abi.is_signed());
// sign extend
let amt = 128 - size;
// shift the unsigned value to the left
// and back to the right as signed (essentially fills with FF on the left)
Ok((((value << amt) as i128) >> amt) as u128)
}
pub fn truncate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
let param_env = ParamEnv::empty();
let layout = tcx.layout_of(param_env.and(ty)).map_err(|layout| EvalErrorKind::Layout(layout))?;
let size = layout.size.bits();
let amt = 128 - size;
// truncate (shift left to drop out leftover values, shift right to fill with zeroes)
Ok((value << amt) >> amt)
}

View File

@ -0,0 +1,19 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-pass
fn main() {
const MIN: i8 = -5;
match 5i8 {
MIN...-1 => {},
_ => {},
}
}