From b272749197a8a4c949b43d4661909b189066f4ae Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Mar 2018 12:38:40 +0100 Subject: [PATCH] Fix the conversion between bit representations and i128 representations --- src/librustc/ty/layout.rs | 8 +++- src/librustc/ty/mod.rs | 23 +++-------- src/librustc/ty/util.rs | 46 ++++++++++++--------- src/test/run-pass/ctfe/signed_enum_discr.rs | 27 ++++++++++++ 4 files changed, 66 insertions(+), 38 deletions(-) create mode 100644 src/test/run-pass/ctfe/signed_enum_discr.rs diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 3a3f10cb87db4..029dd6f1fb466 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -1544,11 +1544,17 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { } let (mut min, mut max) = (i128::max_value(), i128::min_value()); + let discr_type = def.repr.discr_type(); + let bits = Integer::from_attr(tcx, discr_type).size().bits(); for (i, discr) in def.discriminants(tcx).enumerate() { if variants[i].iter().any(|f| f.abi == Abi::Uninhabited) { continue; } - let x = discr.val as i128; + let mut x = discr.val as i128; + if discr_type.is_signed() { + // sign extend the raw representation to be an i128 + x = (x << (128 - bits)) >> (128 - bits); + } if x < min { min = x; } if x > max { max = x; } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index c915022351925..9ffdccefae585 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1886,7 +1886,6 @@ impl<'a, 'gcx, 'tcx> AdtDef { ) -> Option> { let param_env = ParamEnv::empty(); let repr_type = self.repr.discr_type(); - let bit_size = layout::Integer::from_attr(tcx, repr_type).size().bits(); let substs = Substs::identity_for_item(tcx.global_tcx(), expr_did); let instance = ty::Instance::new(expr_did, substs); let cid = GlobalId { @@ -1896,25 +1895,13 @@ impl<'a, 'gcx, 'tcx> AdtDef { match tcx.const_eval(param_env.and(cid)) { Ok(&ty::Const { val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(b))), - .. + ty, }) => { trace!("discriminants: {} ({:?})", b, repr_type); - let ty = repr_type.to_ty(tcx); - if repr_type.is_signed() { - let val = b as i128; - // sign extend to i128 - let amt = 128 - bit_size; - let val = (val << amt) >> amt; - Some(Discr { - val: val as u128, - ty, - }) - } else { - Some(Discr { - val: b, - ty, - }) - } + Some(Discr { + val: b, + ty, + }) }, Ok(&ty::Const { val: ConstVal::Value(other), diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 91d460a96f785..afe977d10baac 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -39,16 +39,24 @@ use syntax_pos::{Span, DUMMY_SP}; #[derive(Copy, Clone, Debug)] pub struct Discr<'tcx> { + /// bit representation of the discriminant, so `-128i8` is `0xFF_u128` pub val: u128, pub ty: Ty<'tcx> } impl<'tcx> fmt::Display for Discr<'tcx> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - if self.ty.is_signed() { - write!(fmt, "{}", self.val as i128) - } else { - write!(fmt, "{}", self.val) + match self.ty.sty { + ty::TyInt(ity) => { + let bits = ty::tls::with(|tcx| { + Integer::from_attr(tcx, SignedInt(ity)).size().bits() + }); + let x = self.val as i128; + // sign extend the raw representation to be an i128 + let x = (x << (128 - bits)) >> (128 - bits); + write!(fmt, "{}", x) + }, + _ => write!(fmt, "{}", self.val), } } } @@ -64,15 +72,18 @@ impl<'tcx> Discr<'tcx> { TyUint(uty) => (Integer::from_attr(tcx, UnsignedInt(uty)), false), _ => bug!("non integer discriminant"), }; + + let bit_size = int.size().bits(); + let amt = 128 - bit_size; if signed { - let (min, max) = match int { - Integer::I8 => (i8::min_value() as i128, i8::max_value() as i128), - Integer::I16 => (i16::min_value() as i128, i16::max_value() as i128), - Integer::I32 => (i32::min_value() as i128, i32::max_value() as i128), - Integer::I64 => (i64::min_value() as i128, i64::max_value() as i128), - Integer::I128 => (i128::min_value(), i128::max_value()), + let sext = |u| { + let i = u as i128; + (i << amt) >> amt }; - let val = self.val as i128; + let min = sext(1_u128 << (bit_size - 1)); + let max = i128::max_value() >> amt; + let val = sext(self.val); + assert!(n < (i128::max_value() as u128)); let n = n as i128; let oflo = val > max - n; let val = if oflo { @@ -80,22 +91,19 @@ impl<'tcx> Discr<'tcx> { } else { val + n }; + // zero the upper bits + let val = val as u128; + let val = (val << amt) >> amt; (Self { val: val as u128, ty: self.ty, }, oflo) } else { - let (min, max) = match int { - Integer::I8 => (u8::min_value() as u128, u8::max_value() as u128), - Integer::I16 => (u16::min_value() as u128, u16::max_value() as u128), - Integer::I32 => (u32::min_value() as u128, u32::max_value() as u128), - Integer::I64 => (u64::min_value() as u128, u64::max_value() as u128), - Integer::I128 => (u128::min_value(), u128::max_value()), - }; + let max = u128::max_value() >> amt; let val = self.val; let oflo = val > max - n; let val = if oflo { - min + (n - (max - val) - 1) + n - (max - val) - 1 } else { val + n }; diff --git a/src/test/run-pass/ctfe/signed_enum_discr.rs b/src/test/run-pass/ctfe/signed_enum_discr.rs new file mode 100644 index 0000000000000..7049d28a87085 --- /dev/null +++ b/src/test/run-pass/ctfe/signed_enum_discr.rs @@ -0,0 +1,27 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/49181 + +#[derive(Eq, PartialEq)] +#[repr(i8)] +pub enum A { + B = -1, + C = 1, +} + +pub const D: A = A::B; + +fn main() { + match A::C { + D => {}, + _ => {} + } +}