Skip to content

Prepare ConstVal for constant propagators and reduce eval_const_expr in MIR #33274

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

Closed
wants to merge 9 commits into from
47 changes: 33 additions & 14 deletions src/librustc/middle/const_val.rs
Original file line number Diff line number Diff line change
@@ -17,18 +17,22 @@ use std::mem::transmute;
use rustc_const_math::*;
use self::ConstVal::*;

use std::collections::BTreeMap;

#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub enum ConstVal {
Float(f64),
Integral(ConstInt),
Str(InternedString),
ByteStr(Rc<Vec<u8>>),
Bool(bool),
Struct(ast::NodeId),
Tuple(ast::NodeId),
Struct(DefId, BTreeMap<ast::Name, ConstVal>),
/// Tuple or Tuple structs
Tuple(Option<DefId>, Vec<ConstVal>),
/// A function pointer
Function(DefId),
Array(ast::NodeId, u64),
Repeat(ast::NodeId, u64),
Array(Vec<ConstVal>),
Repeat(Box<ConstVal>, u64),
Char(char),
/// A value that only occurs in case `eval_const_expr` reported an error. You should never
/// handle this case. Its sole purpose is to allow more errors to be reported instead of
@@ -44,11 +48,26 @@ impl hash::Hash for ConstVal {
Str(ref a) => a.hash(state),
ByteStr(ref a) => a.hash(state),
Bool(a) => a.hash(state),
Struct(a) => a.hash(state),
Tuple(a) => a.hash(state),
Struct(did, ref tree) => {
did.hash(state);
for (name, val) in tree {
name.hash(state);
val.hash(state);
}
},
Tuple(did, ref v) => {
did.hash(state);
for elem in v {
elem.hash(state);
}
},
Function(a) => a.hash(state),
Array(a, n) => { a.hash(state); n.hash(state) },
Repeat(a, n) => { a.hash(state); n.hash(state) },
Array(ref v) => {
for elem in v {
elem.hash(state);
}
}
Repeat(ref a, n) => { a.hash(state); n.hash(state) },
Char(c) => c.hash(state),
Dummy => ().hash(state),
}
@@ -67,11 +86,11 @@ impl PartialEq for ConstVal {
(&Str(ref a), &Str(ref b)) => a == b,
(&ByteStr(ref a), &ByteStr(ref b)) => a == b,
(&Bool(a), &Bool(b)) => a == b,
(&Struct(a), &Struct(b)) => a == b,
(&Tuple(a), &Tuple(b)) => a == b,
(&Struct(a_did, ref a), &Struct(b_did, ref b)) => (a == b) && (a_did == b_did),
(&Tuple(ref a_did, ref a), &Tuple(ref b_did, ref b)) => (a == b) && (a_did == b_did),
(&Function(a), &Function(b)) => a == b,
(&Array(a, an), &Array(b, bn)) => (a == b) && (an == bn),
(&Repeat(a, an), &Repeat(b, bn)) => (a == b) && (an == bn),
(&Array(ref a), &Array(ref b)) => a == b,
(&Repeat(ref a, an), &Repeat(ref b, bn)) => (a == b) && (an == bn),
(&Char(a), &Char(b)) => a == b,
(&Dummy, &Dummy) => true, // FIXME: should this be false?
_ => false,
@@ -89,8 +108,8 @@ impl ConstVal {
Str(_) => "string literal",
ByteStr(_) => "byte string literal",
Bool(_) => "boolean",
Struct(_) => "struct",
Tuple(_) => "tuple",
Struct(..) => "struct",
Tuple(..) => "tuple",
Function(_) => "function definition",
Array(..) => "array",
Repeat(..) => "repeat",
45 changes: 38 additions & 7 deletions src/librustc/mir/repr.rs
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ use std::borrow::{Cow};
use std::fmt::{self, Debug, Formatter, Write};
use std::{iter, u32};
use std::ops::{Index, IndexMut};
use syntax::ast::{self, Name};
use syntax::ast::Name;
use syntax::codemap::Span;

/// Lowered representation of a single function.
@@ -1039,17 +1039,48 @@ fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ConstVal) -> fmt::Result {
}
Bool(b) => write!(fmt, "{:?}", b),
Function(def_id) => write!(fmt, "{}", item_path_str(def_id)),
Struct(node_id) | Tuple(node_id) | Array(node_id, _) | Repeat(node_id, _) =>
write!(fmt, "{}", node_to_string(node_id)),
Struct(def_id, ref tree) => {
write!(fmt, "{}", item_path_str(def_id))?;
if !tree.is_empty() {
write!(fmt, "{{")?;
for (name, val) in tree {
write!(fmt, "{}:", name)?;
fmt_const_val(fmt, val)?;
write!(fmt, ",")?;
}
write!(fmt, "}}")?;
}
Ok(())
},
Tuple(def_id, ref v) => {
if let Some(def_id) = def_id {
write!(fmt, "{}", item_path_str(def_id))?;
}
write!(fmt, "(")?;
for val in v {
fmt_const_val(fmt, val)?;
write!(fmt, ",")?;
}
write!(fmt, ")")
},
Array(ref v) => {
write!(fmt, "[")?;
for val in v {
fmt_const_val(fmt, val)?;
write!(fmt, ",")?;
}
write!(fmt, "]")
},
Repeat(ref v, n) => {
write!(fmt, "[")?;
fmt_const_val(fmt, v)?;
write!(fmt, ";{}]", n)
},
Char(c) => write!(fmt, "{:?}", c),
Dummy => bug!(),
}
}

fn node_to_string(node_id: ast::NodeId) -> String {
ty::tls::with(|tcx| tcx.map.node_to_user_string(node_id))
}

fn item_path_str(def_id: DefId) -> String {
ty::tls::with(|tcx| tcx.item_path_str(def_id))
}
141 changes: 87 additions & 54 deletions src/librustc_const_eval/eval.rs
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@ use syntax::attr::IntType;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::hash_map::Entry::Vacant;
use std::collections::BTreeMap;

use rustc_const_math::*;

@@ -410,9 +411,11 @@ pub enum ErrKind {
IntermediateUnsignedNegative,
/// Expected, Got
TypeMismatch(String, ConstInt),
BadType(ConstVal),
/// target type, got value
BadType(String, ConstVal),
ErroneousReferencedConstant(Box<ConstEvalErr>),
CharCast(ConstInt),
Aggregate(Vec<ConstEvalErr>),
}

impl From<ConstMathErr> for ErrKind {
@@ -471,11 +474,12 @@ impl ConstEvalErr {
format!("mismatched types: expected `{}`, found `{}`",
expected, got.description()).into_cow()
},
BadType(ref i) => format!("value of wrong type: {:?}", i).into_cow(),
BadType(ref ty, ref i) => format!("expected `{}`, found `{:?}`", ty, i).into_cow(),
ErroneousReferencedConstant(_) => "could not evaluate referenced constant".into_cow(),
CharCast(ref got) => {
format!("only `u8` can be cast as `char`, not `{}`", got.description()).into_cow()
},
Aggregate(ref v) => format!("evaluation of {} fields failed", v.len()).into_cow(),
}
}
}
@@ -771,9 +775,7 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
signal!(e, UnimplementedConstVal("enum variants"));
}
}
Def::Struct(..) => {
ConstVal::Struct(e.id)
}
Def::Struct(did) => Struct(did, BTreeMap::new()),
Def::Local(_, id) => {
debug!("Def::Local({:?}): {:?}", id, fn_args);
if let Some(val) = fn_args.and_then(|args| args.get(&id)) {
@@ -791,15 +793,14 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let callee_val = eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args)?;
let did = match callee_val {
Function(did) => did,
Struct(_) => signal!(e, UnimplementedConstVal("tuple struct constructors")),
Struct(..) => signal!(e, UnimplementedConstVal("tuple struct constructors")),
callee => signal!(e, CallOn(callee)),
};
let (decl, result) = if let Some(fn_like) = lookup_const_fn_by_id(tcx, did) {
(fn_like.decl(), &fn_like.body().expr)
} else {
signal!(e, NonConstPath)
};
let result = result.as_ref().expect("const fn has no result expression");
assert_eq!(decl.inputs.len(), args.len());

let mut call_args = NodeMap();
@@ -816,7 +817,11 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
assert!(old.is_none());
}
debug!("const call({:?})", call_args);
eval_const_expr_partial(tcx, &result, ty_hint, Some(&call_args))?
if let &Some(ref result) = result {
eval_const_expr_partial(tcx, &result, ty_hint, Some(&call_args))?
} else {
Tuple(None, Vec::new())
}
},
hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ety, lit.span) {
Ok(val) => val,
@@ -825,12 +830,46 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
hir::ExprBlock(ref block) => {
match block.expr {
Some(ref expr) => eval_const_expr_partial(tcx, &expr, ty_hint, fn_args)?,
None => signal!(e, UnimplementedConstVal("empty block")),
None => Tuple(None, Vec::new()), // unit value
}
}
hir::ExprType(ref e, _) => eval_const_expr_partial(tcx, &e, ty_hint, fn_args)?,
hir::ExprTup(_) => Tuple(e.id),
hir::ExprStruct(..) => Struct(e.id),
hir::ExprTup(ref v) => {
let mut fields = Vec::with_capacity(v.len());
let mut errors = Vec::new();
for field in v {
match eval_const_expr_partial(tcx, field, ty_hint.erase_hint(), fn_args) {
Ok(v) => fields.push(v),
Err(e) => errors.push(e),
}
}
if !errors.is_empty() {
signal!(e, Aggregate(errors));
}
assert_eq!(fields.len(), v.len());
Tuple(None, fields)
},
hir::ExprStruct(_, _, Some(_)) => signal!(e, UnimplementedConstVal("struct base")),
hir::ExprStruct(_, ref fields, None) => {
let def_id = match tcx.def_map.borrow().get(&e.id).map(|def| def.full_def()) {
Some(Def::Struct(def_id)) => def_id,
Some(Def::Variant(..)) => signal!(e, UnimplementedConstVal("enums")),
_ => signal!(e, NonConstPath),
};
let mut new_fields = BTreeMap::new();
let mut errors = Vec::new();
for field in fields {
match eval_const_expr_partial(tcx, &field.expr, ty_hint.erase_hint(), fn_args) {
Ok(f_val) => assert!(new_fields.insert(field.name.node, f_val).is_none()),
Err(e) => errors.push(e),
}
}
if !errors.is_empty() {
signal!(e, Aggregate(errors));
}
assert_eq!(new_fields.len(), fields.len());
Struct(def_id, new_fields)
},
hir::ExprIndex(ref arr, ref idx) => {
if !tcx.sess.features.borrow().const_indexing {
signal!(e, IndexOpFeatureGated);
@@ -845,21 +884,11 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
};
assert_eq!(idx as usize as u64, idx);
match arr {
Array(_, n) if idx >= n => signal!(e, IndexOutOfBounds),
Array(v, n) => if let hir::ExprVec(ref v) = tcx.map.expect_expr(v).node {
assert_eq!(n as usize as u64, n);
eval_const_expr_partial(tcx, &v[idx as usize], ty_hint, fn_args)?
} else {
bug!()
},
Array(ref v) if idx as usize >= v.len() => signal!(e, IndexOutOfBounds),
Array(ref v) => v[idx as usize].clone(),

Repeat(_, n) if idx >= n => signal!(e, IndexOutOfBounds),
Repeat(elem, _) => eval_const_expr_partial(
tcx,
&tcx.map.expect_expr(elem),
ty_hint,
fn_args,
)?,
Repeat(elem, _) => *elem,

ByteStr(ref data) if idx >= data.len() as u64 => signal!(e, IndexOutOfBounds),
ByteStr(data) => {
@@ -872,11 +901,30 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
_ => signal!(e, IndexedNonVec),
}
}
hir::ExprVec(ref v) => Array(e.id, v.len() as u64),
hir::ExprRepeat(_, ref n) => {
hir::ExprVec(ref v) => {
let mut elems = Vec::with_capacity(v.len());
let mut errors = Vec::new();
for elem in v {
match eval_const_expr_partial(tcx, elem, ty_hint.erase_hint(), fn_args) {
Ok(elem) => elems.push(elem),
Err(e) => errors.push(e),
}
}
if !errors.is_empty() {
signal!(e, Aggregate(errors));
}
assert_eq!(elems.len(), v.len());
Array(elems)
},
hir::ExprRepeat(ref elem, ref n) => {
let len_hint = ty_hint.checked_or(tcx.types.usize);
let val_hint = match ty_hint {
ExprTypeChecked => ExprTypeChecked,
UncheckedExprNoHint => UncheckedExprNoHint,
UncheckedExprHint(ty) => UncheckedExprHint(ty.sequence_element_type(tcx)),
};
Repeat(
e.id,
box eval_const_expr_partial(tcx, elem, val_hint, fn_args)?,
match eval_const_expr_partial(tcx, &n, len_hint, fn_args)? {
Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type),
Integral(_) => signal!(e, RepeatCountNotNatural),
@@ -886,40 +934,24 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
},
hir::ExprTupField(ref base, index) => {
let base_hint = ty_hint.erase_hint();
let c = eval_const_expr_partial(tcx, base, base_hint, fn_args)?;
if let Tuple(tup_id) = c {
if let hir::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node {
if index.node < fields.len() {
eval_const_expr_partial(tcx, &fields[index.node], ty_hint, fn_args)?
} else {
signal!(e, TupleIndexOutOfBounds);
}
} else {
bug!()
}
} else {
signal!(base, ExpectedConstTuple);
match eval_const_expr_partial(tcx, base, base_hint, fn_args)? {
Tuple(_, ref v) if index.node >= v.len() => signal!(e, TupleIndexOutOfBounds),
Tuple(_, v) => v[index.node as usize].clone(),
_ => signal!(base, ExpectedConstTuple),
}
}
hir::ExprField(ref base, field_name) => {
let base_hint = ty_hint.erase_hint();
// Get the base expression if it is a struct and it is constant
let c = eval_const_expr_partial(tcx, base, base_hint, fn_args)?;
if let Struct(struct_id) = c {
if let hir::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node {
// Check that the given field exists and evaluate it
// if the idents are compared run-pass/issue-19244 fails
if let Some(f) = fields.iter().find(|f| f.name.node
== field_name.node) {
eval_const_expr_partial(tcx, &f.expr, ty_hint, fn_args)?
match eval_const_expr_partial(tcx, base, base_hint, fn_args)? {
Struct(_, fields) => {
if let Some(f) = fields.get(&field_name.node) {
f.clone()
} else {
signal!(e, MissingStructField);
}
} else {
bug!()
}
} else {
signal!(base, ExpectedConstStruct);
},
_ => signal!(base, ExpectedConstStruct),
}
}
hir::ExprAddrOf(..) => signal!(e, UnimplementedConstVal("address operator")),
@@ -996,7 +1028,8 @@ fn infer<'a, 'tcx>(i: ConstInt,
let int_ty = tcx.enum_repr_type(hints.iter().next());
infer(i, tcx, &int_ty.to_ty(tcx).sty)
},
(_, i) => Err(BadType(ConstVal::Integral(i))),
(&ty::TyParam(_), i) => Ok(i),
(ty, i) => Err(BadType(ty.to_string(), ConstVal::Integral(i))),
}
}

14 changes: 1 addition & 13 deletions src/librustc_mir/hair/cx/expr.rs
Original file line number Diff line number Diff line change
@@ -712,19 +712,7 @@ fn convert_path_expr<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
ref sty => bug!("unexpected sty: {:?}", sty)
},
Def::Const(def_id) |
Def::AssociatedConst(def_id) => {
let substs = Some(cx.tcx.node_id_item_substs(expr.id).substs);
let tcx = cx.tcx.global_tcx();
if let Some((e, _)) = const_eval::lookup_const_by_id(tcx, def_id, substs) {
// FIXME ConstVal can't be yet used with adjustments, as they would be lost.
if !cx.tcx.tables.borrow().adjustments.contains_key(&e.id) {
if let Some(v) = cx.try_const_eval_literal(e) {
return ExprKind::Literal { literal: v };
}
}
}
def_id
}
Def::AssociatedConst(def_id) => def_id,

Def::Static(node_id, _) => return ExprKind::StaticRef {
id: node_id,
15 changes: 0 additions & 15 deletions src/librustc_mir/hair/cx/mod.rs
Original file line number Diff line number Diff line change
@@ -90,21 +90,6 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> {
}
}

pub fn try_const_eval_literal(&mut self, e: &hir::Expr) -> Option<Literal<'tcx>> {
let hint = const_eval::EvalHint::ExprTypeChecked;
let tcx = self.tcx.global_tcx();
const_eval::eval_const_expr_partial(tcx, e, hint, None).ok().and_then(|v| {
match v {
// All of these contain local IDs, unsuitable for storing in MIR.
ConstVal::Struct(_) | ConstVal::Tuple(_) |
ConstVal::Array(..) | ConstVal::Repeat(..) |
ConstVal::Function(_) => None,

_ => Some(Literal::Value { value: v })
}
})
}

pub fn trait_method(&mut self,
trait_def_id: DefId,
method_name: &str,
82 changes: 58 additions & 24 deletions src/librustc_passes/consts.rs
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ use rustc_const_eval::{ConstEvalErr, lookup_const_fn_by_id, compare_lit_exprs};
use rustc_const_eval::{eval_const_expr_partial, lookup_const_by_id};
use rustc_const_eval::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll, Math};
use rustc_const_eval::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath};
use rustc_const_eval::ErrKind::UnresolvedPath;
use rustc_const_eval::ErrKind::{UnresolvedPath, Aggregate};
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_math::{ConstMathErr, Op};
use rustc::hir::def::Def;
@@ -109,14 +109,32 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
}
}
if let Err(err) = eval_const_expr_partial(self.tcx, expr, ExprTypeChecked, None) {
match err.kind {
UnimplementedConstVal(_) => {},
IndexOpFeatureGated => {},
ErroneousReferencedConstant(_) => {},
_ => self.tcx.sess.add_lint(CONST_ERR, expr.id, expr.span,
format!("constant evaluation error: {}. This will \
become a HARD ERROR in the future",
err.description())),
fn ignore(err: &ConstEvalErr) -> bool {
match err.kind {
UnimplementedConstVal(_) => true,
IndexOpFeatureGated => true,
ErroneousReferencedConstant(_) => true,
Aggregate(ref v) => v.iter().all(ignore),
_ => false,
}
}
if !ignore(&err) {
if let Aggregate(ref v) = err.kind {
for err in v {
if !ignore(err) {
self.tcx.sess.add_lint(CONST_ERR, expr.id, err.span,
format!("constant evaluation error: {}. This \
will become a HARD ERROR in the \
future",
err.description()));
}
}
} else {
self.tcx.sess.add_lint(CONST_ERR, expr.id, err.span,
format!("constant evaluation error: {}. This will \
become a HARD ERROR in the future",
err.description()));
}
}
}
self.with_mode(mode, |this| {
@@ -415,21 +433,37 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
}

if self.mode == Mode::Var && !self.qualif.intersects(ConstQualif::NOT_CONST) {
match eval_const_expr_partial(self.tcx, ex, ExprTypeChecked, None) {
Ok(_) => {}
Err(ConstEvalErr { kind: UnimplementedConstVal(_), ..}) |
Err(ConstEvalErr { kind: MiscCatchAll, ..}) |
Err(ConstEvalErr { kind: MiscBinaryOp, ..}) |
Err(ConstEvalErr { kind: NonConstPath, ..}) |
Err(ConstEvalErr { kind: UnresolvedPath, ..}) |
Err(ConstEvalErr { kind: ErroneousReferencedConstant(_), ..}) |
Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shr)), ..}) |
Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shl)), ..}) |
Err(ConstEvalErr { kind: IndexOpFeatureGated, ..}) => {},
Err(msg) => {
self.tcx.sess.add_lint(CONST_ERR, ex.id,
msg.span,
msg.description().into_owned())
fn ignore(err: &ConstEvalErr) -> bool {
match err.kind {
UnimplementedConstVal(_) |
MiscCatchAll |
MiscBinaryOp |
NonConstPath |
UnresolvedPath |
ErroneousReferencedConstant(_) |
Math(ConstMathErr::Overflow(Op::Shr)) |
Math(ConstMathErr::Overflow(Op::Shl)) |
IndexOpFeatureGated => true,
Aggregate(ref v) => v.iter().all(ignore),
_ => false,
}
}
if let Err(msg) = eval_const_expr_partial(self.tcx, ex, ExprTypeChecked, None) {
if !ignore(&msg) {
if let Aggregate(ref v) = msg.kind {
// report single errors for aggregate errors
for msg in v {
if !ignore(&msg) {
self.tcx.sess.add_lint(CONST_ERR, ex.id,
msg.span,
msg.description().into_owned())
}
}
} else {
self.tcx.sess.add_lint(CONST_ERR, ex.id,
msg.span,
msg.description().into_owned())
}
}
}
}
66 changes: 61 additions & 5 deletions src/librustc_trans/mir/constant.rs
Original file line number Diff line number Diff line change
@@ -17,14 +17,14 @@ use rustc::infer::TransNormalize;
use rustc::mir::repr as mir;
use rustc::mir::tcx::LvalueTy;
use rustc::traits;
use rustc::ty::{self, Ty, TypeFoldable};
use rustc::ty::{self, Ty, TypeFoldable, TyStruct, TyTuple};
use rustc::ty::cast::{CastTy, IntTy};
use rustc::ty::subst::Substs;
use {abi, adt, base, Disr};
use callee::Callee;
use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty};
use common::{C_array, C_bool, C_bytes, C_floating_f64, C_integral};
use common::{C_null, C_struct, C_str_slice, C_undef, C_uint};
use common::{C_null, C_struct, C_str_slice, C_undef, C_uint, C_vector};
use consts::{self, ConstEvalFailure, TrueConst, to_const_int};
use monomorphize::{self, Instance};
use type_of;
@@ -82,10 +82,66 @@ impl<'tcx> Const<'tcx> {
},
ConstVal::Integral(Infer(v)) => C_integral(llty, v as u64, false),
ConstVal::Integral(InferSigned(v)) => C_integral(llty, v as u64, true),
ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()),
ConstVal::Str(v) => C_str_slice(ccx, v),
ConstVal::ByteStr(ref v) => consts::addr_of(ccx, C_bytes(ccx, v), 1, "byte_str"),
ConstVal::Struct(_) | ConstVal::Tuple(_) |
ConstVal::Array(..) | ConstVal::Repeat(..) |
ConstVal::Struct(did, mut field_values) => {
let repr = adt::represent_type(ccx, ty);
let substs = match ty.sty {
TyStruct(_, substs) => substs,
_ => bug!(),
};
let mut trans_fields = Vec::with_capacity(field_values.len());
let adt_def = ty.ty_adt_def().unwrap().struct_variant();
assert_eq!(adt_def.did, did);
for field in &adt_def.fields {
if let Some(value) = field_values.remove(&field.name) {
let field_ty = field.ty(ccx.tcx(), substs);
let value = Self::from_constval(ccx, value, field_ty);
trans_fields.push(value.llval);
} else {
bug!("trans knows struct fields that const doesn't");
}
}
// FIXME: check that all elements of `field_values` have been translated
if ty.is_simd() {
C_vector(&trans_fields)
} else {
adt::trans_const(ccx, &*repr, adt_def.disr_val.into(), &trans_fields)
}
},
ConstVal::Tuple(Some(_), _) => unimplemented!(),
ConstVal::Tuple(None, field_values) => {
let repr = adt::represent_type(ccx, ty);
let field_types = match ty.sty {
TyTuple(types) => types,
_ => bug!(),
};
let mut trans_fields = Vec::with_capacity(field_values.len());
for (f_val, f_ty) in field_values.into_iter().zip(field_types) {
let value = Self::from_constval(ccx, f_val, f_ty);
trans_fields.push(value.llval);
}
if ty.is_simd() {
C_vector(&trans_fields)
} else {
adt::trans_const(ccx, &*repr, Disr(0), &trans_fields)
}
},
ConstVal::Repeat(val, n) => {
let val_ty = ty.sequence_element_type(ccx.tcx());
let ll_val_ty = type_of::type_of(ccx, val_ty);
assert_eq!(n as usize as u64, n);
let trans_fields = vec![Self::from_constval(ccx, *val, val_ty).llval; n as usize];
C_array(ll_val_ty, &trans_fields)
},
ConstVal::Array(vals) => {
let val_ty = ty.sequence_element_type(ccx.tcx());
let ll_val_ty = type_of::type_of(ccx, val_ty);
let trans_fields = vals.into_iter()
.map(|val| Self::from_constval(ccx, val, val_ty).llval)
.collect::<Vec<_>>();
C_array(ll_val_ty, &trans_fields)
},
ConstVal::Function(_) => {
bug!("MIR must not use {:?} (which refers to a local ID)", cv)
}
1 change: 1 addition & 0 deletions src/test/compile-fail/const-eval-overflow.rs
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@

#![feature(rustc_attrs)]
#![allow(unused_imports)]
#![allow(const_err)]

// Note: the relevant lint pass here runs before some of the constant
// evaluation below (e.g. that performed by trans and llvm), so if you
56 changes: 28 additions & 28 deletions src/test/compile-fail/const-eval-overflow0.rs
Original file line number Diff line number Diff line change
@@ -20,67 +20,67 @@ use std::{i8, i16, i32, i64, isize};
use std::{u8, u16, u32, u64, usize};

const VALS_I8: (i8, i8, i8, i8) =
(-i8::MIN,
i8::MIN - 1,
i8::MAX + 1,
i8::MIN * 2,
(-i8::MIN, //~WARN const_err
i8::MIN - 1, //~WARN const_err
i8::MAX + 1, //~WARN const_err
i8::MIN * 2, //~WARN const_err
);

const VALS_I16: (i16, i16, i16, i16) =
(-i16::MIN,
i16::MIN - 1,
i16::MAX + 1,
i16::MIN * 2,
(-i16::MIN, //~WARN const_err
i16::MIN - 1, //~WARN const_err
i16::MAX + 1, //~WARN const_err
i16::MIN * 2, //~WARN const_err
);

const VALS_I32: (i32, i32, i32, i32) =
(-i32::MIN,
i32::MIN - 1,
i32::MAX + 1,
i32::MIN * 2,
(-i32::MIN, //~WARN const_err
i32::MIN - 1, //~WARN const_err
i32::MAX + 1, //~WARN const_err
i32::MIN * 2, //~WARN const_err
);

const VALS_I64: (i64, i64, i64, i64) =
(-i64::MIN,
i64::MIN - 1,
i64::MAX + 1,
i64::MAX * 2,
(-i64::MIN, //~WARN const_err
i64::MIN - 1, //~WARN const_err
i64::MAX + 1, //~WARN const_err
i64::MAX * 2, //~WARN const_err
);

const VALS_U8: (u8, u8, u8, u8) =
(-u8::MIN,
//~^ ERROR unary negation of unsigned integer
//~| HELP use a cast or the `!` operator
u8::MIN - 1,
u8::MAX + 1,
u8::MAX * 2,
u8::MIN - 1, //~WARN const_err
u8::MAX + 1, //~WARN const_err
u8::MAX * 2, //~WARN const_err
);

const VALS_U16: (u16, u16, u16, u16) =
(-u16::MIN,
//~^ ERROR unary negation of unsigned integer
//~| HELP use a cast or the `!` operator
u16::MIN - 1,
u16::MAX + 1,
u16::MAX * 2,
u16::MIN - 1, //~WARN const_err
u16::MAX + 1, //~WARN const_err
u16::MAX * 2, //~WARN const_err
);

const VALS_U32: (u32, u32, u32, u32) =
(-u32::MIN,
//~^ ERROR unary negation of unsigned integer
//~| HELP use a cast or the `!` operator
u32::MIN - 1,
u32::MAX + 1,
u32::MAX * 2,
u32::MIN - 1, //~WARN const_err
u32::MAX + 1, //~WARN const_err
u32::MAX * 2, //~WARN const_err
);

const VALS_U64: (u64, u64, u64, u64) =
(-u64::MIN,
//~^ ERROR unary negation of unsigned integer
//~| HELP use a cast or the `!` operator
u64::MIN - 1,
u64::MAX + 1,
u64::MAX * 2,
u64::MIN - 1, //~WARN const_err
u64::MAX + 1, //~WARN const_err
u64::MAX * 2, //~WARN const_err
);

fn main() {
3 changes: 1 addition & 2 deletions src/test/compile-fail/const-pattern-not-const-evaluable.rs
Original file line number Diff line number Diff line change
@@ -17,8 +17,7 @@ enum Cake {
use Cake::*;

const BOO: (Cake, Cake) = (Marmor, BlackForest);
//~^ ERROR: constant evaluation error: unimplemented constant expression: enum variants [E0471]
const FOO: Cake = BOO.1;
const FOO: Cake = BOO.1; //~ERROR could not evaluate referenced constant

const fn foo() -> Cake {
Marmor //~ ERROR: constant evaluation error: unimplemented constant expression: enum variants
3 changes: 3 additions & 0 deletions src/test/compile-fail/issue-17718-const-borrow.rs
Original file line number Diff line number Diff line change
@@ -23,4 +23,7 @@ const E: &'static UnsafeCell<usize> = &D.a;
const F: &'static C = &D;
//~^ ERROR: cannot borrow a constant which contains interior mutability

const G: &'static UnsafeCell<usize> = &UnsafeCell::new(42);
//~^ ERROR: cannot borrow a constant which contains interior mutability

fn main() {}
15 changes: 15 additions & 0 deletions src/test/run-pass/const-fn.rs
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
// A very basic test of const fn functionality.

#![feature(const_fn, const_indexing)]
#![deny(const_err)]

const fn add(x: u32, y: u32) -> u32 {
x + y
@@ -32,6 +33,18 @@ const fn generic_arr<T: Copy>(t: [T; 1]) -> T {
t[0]
}

pub const fn test() {}
const X: () = test();

const fn f(_: ()) -> usize { 1 }

const fn g(x: usize) -> A {
A { field: x }
}
struct A {
field: usize,
}

const SUM: u32 = add(44, 22);
const DIFF: u32 = sub(44, 22);
const DIV: u32 = unsafe{div(44, 22)};
@@ -46,4 +59,6 @@ fn main() {
let _: [&'static str; sub(100, 99) as usize] = ["hi"];
let _: [&'static str; generic(1)] = ["hi"];
let _: [&'static str; generic_arr([1])] = ["hi"];
let _: [&'static str; f({})] = ["hi"];
let _ = [0; g(5).field];
}
2 changes: 1 addition & 1 deletion src/test/run-pass/issue-28189.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

struct S<T>(T) where [T; (||{}, 1).1]: Copy;
struct S<T>(T) where [T; ((), 1).1]: Copy;

fn main() {