Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deduplicate literal -> constant lowering #56312

Merged
merged 2 commits into from
Nov 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions src/librustc_mir/hair/constant.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use syntax::ast;
use rustc::ty::{self, Ty, TyCtxt, ParamEnv};
use syntax_pos::symbol::Symbol;
use rustc::mir::interpret::{ConstValue, Scalar};

#[derive(PartialEq)]
crate enum LitToConstError {
UnparseableFloat,
Reported,
}

crate fn lit_to_const<'a, 'gcx, 'tcx>(
lit: &'tcx ast::LitKind,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
ty: Ty<'tcx>,
neg: bool,
) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> {
use syntax::ast::*;

let trunc = |n| {
let param_ty = ParamEnv::reveal_all().and(tcx.lift_to_global(&ty).unwrap());
let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size;
trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
let shift = 128 - width.bits();
let result = (n << shift) >> shift;
trace!("trunc result: {}", result);
Ok(ConstValue::Scalar(Scalar::Bits {
bits: result,
size: width.bytes() as u8,
}))
};

use rustc::mir::interpret::*;
let lit = match *lit {
LitKind::Str(ref s, _) => {
let s = s.as_str();
let id = tcx.allocate_bytes(s.as_bytes());
ConstValue::new_slice(Scalar::Ptr(id.into()), s.len() as u64, &tcx)
},
LitKind::ByteStr(ref data) => {
let id = tcx.allocate_bytes(data);
ConstValue::Scalar(Scalar::Ptr(id.into()))
},
LitKind::Byte(n) => ConstValue::Scalar(Scalar::Bits {
bits: n as u128,
size: 1,
}),
LitKind::Int(n, _) if neg => {
let n = n as i128;
let n = n.overflowing_neg().0;
trunc(n as u128)?
},
LitKind::Int(n, _) => trunc(n)?,
LitKind::Float(n, fty) => {
parse_float(n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat)?
}
LitKind::FloatUnsuffixed(n) => {
let fty = match ty.sty {
ty::Float(fty) => fty,
_ => bug!()
};
parse_float(n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat)?
}
LitKind::Bool(b) => ConstValue::Scalar(Scalar::from_bool(b)),
LitKind::Char(c) => ConstValue::Scalar(Scalar::from_char(c)),
};
Ok(ty::Const::from_const_value(tcx, lit, ty))
}

fn parse_float<'tcx>(
num: Symbol,
fty: ast::FloatTy,
neg: bool,
) -> Result<ConstValue<'tcx>, ()> {
let num = num.as_str();
use rustc_apfloat::ieee::{Single, Double};
use rustc_apfloat::Float;
let (bits, size) = match fty {
ast::FloatTy::F32 => {
num.parse::<f32>().map_err(|_| ())?;
let mut f = num.parse::<Single>().unwrap_or_else(|e| {
panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e)
});
if neg {
f = -f;
}
(f.to_bits(), 4)
}
ast::FloatTy::F64 => {
num.parse::<f64>().map_err(|_| ())?;
let mut f = num.parse::<Double>().unwrap_or_else(|e| {
panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e)
});
if neg {
f = -f;
}
(f.to_bits(), 8)
}
};

Ok(ConstValue::Scalar(Scalar::Bits { bits, size }))
}
67 changes: 12 additions & 55 deletions src/librustc_mir/hair/cx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ use rustc::ty::subst::Subst;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::subst::{Kind, Substs};
use rustc::ty::layout::VariantIdx;
use syntax::ast::{self, LitKind};
use syntax::ast;
use syntax::attr;
use syntax::symbol::Symbol;
use rustc::hir;
use rustc_data_structures::sync::Lrc;
use hair::pattern::parse_float;
use hair::constant::{lit_to_const, LitToConstError};

#[derive(Clone)]
pub struct Cx<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
Expand Down Expand Up @@ -131,7 +131,6 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
ty::Const::from_bool(self.tcx, false)
}

// FIXME: Combine with rustc_mir::hair::pattern::lit_to_const
pub fn const_eval_literal(
&mut self,
lit: &'tcx ast::LitKind,
Expand All @@ -141,61 +140,19 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
) -> &'tcx ty::Const<'tcx> {
trace!("const_eval_literal: {:#?}, {:?}, {:?}, {:?}", lit, ty, sp, neg);

let parse_float = |num, fty| -> ConstValue<'tcx> {
parse_float(num, fty, neg).unwrap_or_else(|_| {
match lit_to_const(lit, self.tcx, ty, neg) {
Ok(c) => c,
Err(LitToConstError::UnparseableFloat) => {
// FIXME(#31407) this is only necessary because float parsing is buggy
self.tcx.sess.span_fatal(sp, "could not evaluate float literal (see issue #31407)");
})
};

let trunc = |n| {
let param_ty = self.param_env.and(self.tcx.lift_to_global(&ty).unwrap());
let width = self.tcx.layout_of(param_ty).unwrap().size;
trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
let shift = 128 - width.bits();
let result = (n << shift) >> shift;
trace!("trunc result: {}", result);
ConstValue::Scalar(Scalar::Bits {
bits: result,
size: width.bytes() as u8,
})
};

use rustc::mir::interpret::*;
let lit = match *lit {
LitKind::Str(ref s, _) => {
let s = s.as_str();
let id = self.tcx.allocate_bytes(s.as_bytes());
ConstValue::new_slice(Scalar::Ptr(id.into()), s.len() as u64, &self.tcx)
},
LitKind::ByteStr(ref data) => {
let id = self.tcx.allocate_bytes(data);
ConstValue::Scalar(Scalar::Ptr(id.into()))
self.tcx.sess.span_err(sp, "could not evaluate float literal (see issue #31407)");
// create a dummy value and continue compiling
Const::from_bits(self.tcx, 0, self.param_env.and(ty))
},
LitKind::Byte(n) => ConstValue::Scalar(Scalar::Bits {
bits: n as u128,
size: 1,
}),
LitKind::Int(n, _) if neg => {
let n = n as i128;
let n = n.overflowing_neg().0;
trunc(n as u128)
},
LitKind::Int(n, _) => trunc(n),
LitKind::Float(n, fty) => {
parse_float(n, fty)
}
LitKind::FloatUnsuffixed(n) => {
let fty = match ty.sty {
ty::Float(fty) => fty,
_ => bug!()
};
parse_float(n, fty)
Err(LitToConstError::Reported) => {
// create a dummy value and continue compiling
Const::from_bits(self.tcx, 0, self.param_env.and(ty))
}
LitKind::Bool(b) => ConstValue::Scalar(Scalar::from_bool(b)),
LitKind::Char(c) => ConstValue::Scalar(Scalar::from_char(c)),
};
ty::Const::from_const_value(self.tcx, lit, ty)
}
}

pub fn pattern_from_hir(&mut self, p: &hir::Pat) -> Pattern<'tcx> {
Expand Down
1 change: 1 addition & 0 deletions src/librustc_mir/hair/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use syntax_pos::Span;
use self::cx::Cx;

pub mod cx;
mod constant;

pub mod pattern;
pub use self::pattern::{BindingMode, Pattern, PatternKind, FieldPattern};
Expand Down
137 changes: 7 additions & 130 deletions src/librustc_mir/hair/pattern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub(crate) use self::check_match::check_match;
use const_eval::{const_field, const_variant_index};

use hair::util::UserAnnotatedTyHelpers;
use hair::constant::*;

use rustc::mir::{fmt_const_val, Field, BorrowKind, Mutability};
use rustc::mir::{ProjectionElem, UserTypeAnnotation, UserTypeProjection, UserTypeProjections};
Expand All @@ -37,7 +38,6 @@ use std::fmt;
use syntax::ast;
use syntax::ptr::P;
use syntax_pos::Span;
use syntax_pos::symbol::Symbol;

#[derive(Clone, Debug)]
pub enum PatternError {
Expand Down Expand Up @@ -891,12 +891,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
);
*self.const_to_pat(instance, val, expr.hir_id, lit.span).kind
},
Err(e) => {
if e == LitToConstError::UnparseableFloat {
self.errors.push(PatternError::FloatBug);
}
Err(LitToConstError::UnparseableFloat) => {
self.errors.push(PatternError::FloatBug);
PatternKind::Wild
},
Err(LitToConstError::Reported) => PatternKind::Wild,
}
},
hir::ExprKind::Path(ref qpath) => *self.lower_path(qpath, expr.hir_id, expr.span).kind,
Expand All @@ -914,12 +913,11 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
);
*self.const_to_pat(instance, val, expr.hir_id, lit.span).kind
},
Err(e) => {
if e == LitToConstError::UnparseableFloat {
self.errors.push(PatternError::FloatBug);
}
Err(LitToConstError::UnparseableFloat) => {
self.errors.push(PatternError::FloatBug);
PatternKind::Wild
},
Err(LitToConstError::Reported) => PatternKind::Wild,
}
}
_ => span_bug!(expr.span, "not a literal: {:?}", expr),
Expand Down Expand Up @@ -1294,124 +1292,3 @@ pub fn compare_const_vals<'a, 'tcx>(

fallback()
}

#[derive(PartialEq)]
enum LitToConstError {
UnparseableFloat,
Propagated,
}

// FIXME: Combine with rustc_mir::hair::cx::const_eval_literal
fn lit_to_const<'a, 'tcx>(lit: &'tcx ast::LitKind,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty: Ty<'tcx>,
neg: bool)
-> Result<&'tcx ty::Const<'tcx>, LitToConstError> {
use syntax::ast::*;

use rustc::mir::interpret::*;
let lit = match *lit {
LitKind::Str(ref s, _) => {
let s = s.as_str();
let id = tcx.allocate_bytes(s.as_bytes());
ConstValue::new_slice(Scalar::Ptr(id.into()), s.len() as u64, &tcx)
},
LitKind::ByteStr(ref data) => {
let id = tcx.allocate_bytes(data);
ConstValue::Scalar(Scalar::Ptr(id.into()))
},
LitKind::Byte(n) => ConstValue::Scalar(Scalar::Bits {
bits: n as u128,
size: 1,
}),
LitKind::Int(n, _) => {
enum Int {
Signed(IntTy),
Unsigned(UintTy),
}
let ity = match ty.sty {
ty::Int(IntTy::Isize) => Int::Signed(tcx.sess.target.isize_ty),
ty::Int(other) => Int::Signed(other),
ty::Uint(UintTy::Usize) => Int::Unsigned(tcx.sess.target.usize_ty),
ty::Uint(other) => Int::Unsigned(other),
ty::Error => { // Avoid ICE (#51963)
return Err(LitToConstError::Propagated);
}
_ => bug!("literal integer type with bad type ({:?})", ty.sty),
};
// This converts from LitKind::Int (which is sign extended) to
// Scalar::Bytes (which is zero extended)
let n = match ity {
// FIXME(oli-obk): are these casts correct?
Int::Signed(IntTy::I8) if neg =>
(n as i8).overflowing_neg().0 as u8 as u128,
Int::Signed(IntTy::I16) if neg =>
(n as i16).overflowing_neg().0 as u16 as u128,
Int::Signed(IntTy::I32) if neg =>
(n as i32).overflowing_neg().0 as u32 as u128,
Int::Signed(IntTy::I64) if neg =>
(n as i64).overflowing_neg().0 as u64 as u128,
Int::Signed(IntTy::I128) if neg =>
(n as i128).overflowing_neg().0 as u128,
Int::Signed(IntTy::I8) | Int::Unsigned(UintTy::U8) => n as u8 as u128,
Int::Signed(IntTy::I16) | Int::Unsigned(UintTy::U16) => n as u16 as u128,
Int::Signed(IntTy::I32) | Int::Unsigned(UintTy::U32) => n as u32 as u128,
Int::Signed(IntTy::I64) | Int::Unsigned(UintTy::U64) => n as u64 as u128,
Int::Signed(IntTy::I128)| Int::Unsigned(UintTy::U128) => n,
_ => bug!(),
};
let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size.bytes() as u8;
ConstValue::Scalar(Scalar::Bits {
bits: n,
size,
})
},
LitKind::Float(n, fty) => {
parse_float(n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat)?
}
LitKind::FloatUnsuffixed(n) => {
let fty = match ty.sty {
ty::Float(fty) => fty,
_ => bug!()
};
parse_float(n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat)?
}
LitKind::Bool(b) => ConstValue::Scalar(Scalar::from_bool(b)),
LitKind::Char(c) => ConstValue::Scalar(Scalar::from_char(c)),
};
Ok(ty::Const::from_const_value(tcx, lit, ty))
}

pub fn parse_float<'tcx>(
num: Symbol,
fty: ast::FloatTy,
neg: bool,
) -> Result<ConstValue<'tcx>, ()> {
let num = num.as_str();
use rustc_apfloat::ieee::{Single, Double};
use rustc_apfloat::Float;
let (bits, size) = match fty {
ast::FloatTy::F32 => {
num.parse::<f32>().map_err(|_| ())?;
let mut f = num.parse::<Single>().unwrap_or_else(|e| {
panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e)
});
if neg {
f = -f;
}
(f.to_bits(), 4)
}
ast::FloatTy::F64 => {
num.parse::<f64>().map_err(|_| ())?;
let mut f = num.parse::<Double>().unwrap_or_else(|e| {
panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e)
});
if neg {
f = -f;
}
(f.to_bits(), 8)
}
};

Ok(ConstValue::Scalar(Scalar::Bits { bits, size }))
}