Skip to content
47 changes: 33 additions & 14 deletions src/librustc/middle/const_val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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),
}
Expand All @@ -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,
Expand All @@ -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",
Expand Down
45 changes: 38 additions & 7 deletions src/librustc/mir/repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Up @@ -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::*;

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(),
}
}
}
Expand Down Expand Up @@ -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)) {
Expand All @@ -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();
Expand All @@ -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,
Expand All @@ -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);
Expand All @@ -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) => {
Expand All @@ -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),
Expand All @@ -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")),
Expand Down Expand Up @@ -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))),
}
}

Expand Down
Loading