Skip to content

Commit

Permalink
Auto merge of #49949 - oli-obk:const_signed_pat, r=eddyb
Browse files Browse the repository at this point in the history
Sign extend constants in range patterns

fixes  #49940

r? @Mark-Simulacrum
  • Loading branch information
bors committed Apr 19, 2018
2 parents 78fc510 + b22c9c0 commit 5fe6b58
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 23 deletions.
18 changes: 11 additions & 7 deletions src/librustc_mir/hair/pattern/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
) {
Expand All @@ -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![]),
Expand Down
17 changes: 13 additions & 4 deletions src/librustc_mir/hair/pattern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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, _) => {
Expand Down Expand Up @@ -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};
Expand All @@ -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)),
}
},
Expand Down
14 changes: 2 additions & 12 deletions src/librustc_mir/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
24 changes: 24 additions & 0 deletions src/librustc_mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
19 changes: 19 additions & 0 deletions src/test/ui/const-eval/const_signed_pat.rs
Original file line number Diff line number Diff line change
@@ -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 => {},
_ => {},
}
}

0 comments on commit 5fe6b58

Please sign in to comment.