From d2cc076ca2045349cd6d1b250ddafd0a29c1ff21 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 29 Apr 2016 14:36:27 +0200 Subject: [PATCH 1/9] aggregate constants store their interior values instead of an expression id --- src/librustc/middle/const_val.rs | 45 +++++-- src/librustc/mir/repr.rs | 45 ++++++- src/librustc_const_eval/eval.rs | 123 +++++++++++------- src/librustc_passes/consts.rs | 82 ++++++++---- src/librustc_trans/mir/constant.rs | 64 ++++++++- src/test/compile-fail/const-eval-overflow.rs | 1 + src/test/compile-fail/const-eval-overflow0.rs | 56 ++++---- .../const-pattern-not-const-evaluable.rs | 3 +- src/test/run-pass/issue-28189.rs | 2 +- 9 files changed, 293 insertions(+), 128 deletions(-) diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 3621cb267d91f..116f093a61523 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -17,6 +17,8 @@ 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), @@ -24,11 +26,11 @@ pub enum ConstVal { Str(InternedString), ByteStr(Rc>), Bool(bool), - Struct(ast::NodeId), - Tuple(ast::NodeId), + Struct(DefId, BTreeMap), + Tuple(Option, Vec), Function(DefId), - Array(ast::NodeId, u64), - Repeat(ast::NodeId, u64), + Array(Vec), + Repeat(Box, 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 +46,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 +84,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 +106,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", diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 458cb28144adb..ab2f21fe822b9 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -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(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)) } diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 9db24fa4770fe..fbeb81586027f 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -413,6 +413,7 @@ pub enum ErrKind { BadType(ConstVal), ErroneousReferencedConstant(Box), CharCast(ConstInt), + Aggregate(Vec), } impl From for ErrKind { @@ -476,6 +477,7 @@ impl ConstEvalErr { 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 +773,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,7 +791,7 @@ 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) { @@ -829,8 +829,42 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } 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 +879,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 +896,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 +929,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")), diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index b1bb48aacee9f..9fce6e6fee8b7 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -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()) + } } } } diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 039304ece60b0..ee9f0fe856703 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -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; @@ -84,8 +84,64 @@ impl<'tcx> Const<'tcx> { ConstVal::Integral(InferSigned(v)) => C_integral(llty, v as u64, true), ConstVal::Str(ref v) => C_str_slice(ccx, v.clone()), 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(ref 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::>(); + C_array(ll_val_ty, &trans_fields) + }, ConstVal::Function(_) => { bug!("MIR must not use {:?} (which refers to a local ID)", cv) } diff --git a/src/test/compile-fail/const-eval-overflow.rs b/src/test/compile-fail/const-eval-overflow.rs index 96013551ef492..7efe309484579 100644 --- a/src/test/compile-fail/const-eval-overflow.rs +++ b/src/test/compile-fail/const-eval-overflow.rs @@ -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 diff --git a/src/test/compile-fail/const-eval-overflow0.rs b/src/test/compile-fail/const-eval-overflow0.rs index 7db7de9cee30c..e7153641f47c5 100644 --- a/src/test/compile-fail/const-eval-overflow0.rs +++ b/src/test/compile-fail/const-eval-overflow0.rs @@ -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() { diff --git a/src/test/compile-fail/const-pattern-not-const-evaluable.rs b/src/test/compile-fail/const-pattern-not-const-evaluable.rs index 4567cd4a74bb2..cb55e81eb9f21 100644 --- a/src/test/compile-fail/const-pattern-not-const-evaluable.rs +++ b/src/test/compile-fail/const-pattern-not-const-evaluable.rs @@ -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 diff --git a/src/test/run-pass/issue-28189.rs b/src/test/run-pass/issue-28189.rs index 0e624a778583d..032b809bfcafe 100644 --- a/src/test/run-pass/issue-28189.rs +++ b/src/test/run-pass/issue-28189.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct S(T) where [T; (||{}, 1).1]: Copy; +struct S(T) where [T; ((), 1).1]: Copy; fn main() { From a43598c57f90805c0091e5ba2228991e2b20533c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 29 Apr 2016 14:37:37 +0200 Subject: [PATCH 2/9] evaluation of generic const fns can run into trouble with inferring integrals --- src/librustc_const_eval/eval.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index fbeb81586027f..dd93085d67277 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -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,7 +411,8 @@ pub enum ErrKind { IntermediateUnsignedNegative, /// Expected, Got TypeMismatch(String, ConstInt), - BadType(ConstVal), + /// target type, got value + BadType(String, ConstVal), ErroneousReferencedConstant(Box), CharCast(ConstInt), Aggregate(Vec), @@ -472,7 +474,7 @@ 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() @@ -1023,7 +1025,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))), } } From c40a7f1cc7eb8ebfaf91004918e353c035653a89 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 29 Apr 2016 14:38:17 +0200 Subject: [PATCH 3/9] check an additional variant of constant borrowing of interior mutable objects --- src/test/compile-fail/issue-17718-const-borrow.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/compile-fail/issue-17718-const-borrow.rs b/src/test/compile-fail/issue-17718-const-borrow.rs index ec6d1141c1a05..88967b7669bfb 100644 --- a/src/test/compile-fail/issue-17718-const-borrow.rs +++ b/src/test/compile-fail/issue-17718-const-borrow.rs @@ -23,4 +23,7 @@ const E: &'static UnsafeCell = &D.a; const F: &'static C = &D; //~^ ERROR: cannot borrow a constant which contains interior mutability +const G: &'static UnsafeCell = &UnsafeCell::new(42); +//~^ ERROR: cannot borrow a constant which contains interior mutability + fn main() {} From e36c186d4f26d4f1013ac7501804067995ba6a96 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 29 Apr 2016 14:38:26 +0200 Subject: [PATCH 4/9] no ref + clone necessary --- src/librustc_trans/mir/constant.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index ee9f0fe856703..6c31b05a87c18 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -82,7 +82,7 @@ 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(did, mut field_values) => { let repr = adt::represent_type(ccx, ty); From cc05dfe620f38b29b8b65fe141afcde8e6ab9ae3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 29 Apr 2016 14:40:08 +0200 Subject: [PATCH 5/9] don't const eval constants during MIR creation this didn't work very well anyway, because const_eval can't eval all kinds of constants. --- src/librustc_mir/hair/cx/expr.rs | 14 +------------- src/librustc_mir/hair/cx/mod.rs | 15 --------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 1e7164a62c070..fd07439c43a3d 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -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, diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index fad6cfb7ae1aa..e5d2b00add767 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -90,21 +90,6 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { } } - pub fn try_const_eval_literal(&mut self, e: &hir::Expr) -> Option> { - 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, From cf45924d5cf8cd2134b3f8cb85e09a304c908523 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 29 Apr 2016 15:56:02 +0200 Subject: [PATCH 6/9] fix `{}` in constants fixes #31384 --- src/librustc/middle/const_val.rs | 2 ++ src/librustc_const_eval/eval.rs | 2 +- src/test/run-pass/const-fn.rs | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/librustc/middle/const_val.rs b/src/librustc/middle/const_val.rs index 116f093a61523..2e7e9335ed118 100644 --- a/src/librustc/middle/const_val.rs +++ b/src/librustc/middle/const_val.rs @@ -27,7 +27,9 @@ pub enum ConstVal { ByteStr(Rc>), Bool(bool), Struct(DefId, BTreeMap), + /// Tuple or Tuple structs Tuple(Option, Vec), + /// A function pointer Function(DefId), Array(Vec), Repeat(Box, u64), diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index dd93085d67277..add8fdafa1d73 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -827,7 +827,7 @@ 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)?, diff --git a/src/test/run-pass/const-fn.rs b/src/test/run-pass/const-fn.rs index 562040dc5620b..0b429904e9701 100644 --- a/src/test/run-pass/const-fn.rs +++ b/src/test/run-pass/const-fn.rs @@ -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,8 @@ const fn generic_arr(t: [T; 1]) -> T { t[0] } +const fn f(_: ()) -> usize { 1 } + const SUM: u32 = add(44, 22); const DIFF: u32 = sub(44, 22); const DIV: u32 = unsafe{div(44, 22)}; @@ -46,4 +49,5 @@ 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"]; } From 094d95b52db1a6920412a1cca04ece7b2ba1b71e Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 29 Apr 2016 15:56:47 +0200 Subject: [PATCH 7/9] fix calling const fns with an empty body fixes #33031 --- src/librustc_const_eval/eval.rs | 7 +++++-- src/test/run-pass/const-fn.rs | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index add8fdafa1d73..d2cd50b3d606c 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -801,7 +801,6 @@ pub fn eval_const_expr_partial<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } 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(); @@ -818,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, diff --git a/src/test/run-pass/const-fn.rs b/src/test/run-pass/const-fn.rs index 0b429904e9701..a0a68533b58ad 100644 --- a/src/test/run-pass/const-fn.rs +++ b/src/test/run-pass/const-fn.rs @@ -33,6 +33,9 @@ const fn generic_arr(t: [T; 1]) -> T { t[0] } +pub const fn test() {} +const X: () = test(); + const fn f(_: ()) -> usize { 1 } const SUM: u32 = add(44, 22); @@ -50,4 +53,5 @@ fn main() { let _: [&'static str; generic(1)] = ["hi"]; let _: [&'static str; generic_arr([1])] = ["hi"]; let _: [&'static str; f({})] = ["hi"]; + let _ = [0; g(5).field]; } From 6ac3812ca5385b2285080f1987e657e4216bc181 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 29 Apr 2016 15:57:12 +0200 Subject: [PATCH 8/9] add a test for #29928 --- src/test/run-pass/const-fn.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/run-pass/const-fn.rs b/src/test/run-pass/const-fn.rs index a0a68533b58ad..3b13cc661f6ee 100644 --- a/src/test/run-pass/const-fn.rs +++ b/src/test/run-pass/const-fn.rs @@ -38,6 +38,13 @@ 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)}; From ce70c5f0affcf6fa08ff8f845fce65c0bf3f25f5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 11 May 2016 19:05:15 +0200 Subject: [PATCH 9/9] rebased --- src/librustc_trans/mir/constant.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 6c31b05a87c18..b3c89708f5a4e 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -113,7 +113,7 @@ impl<'tcx> Const<'tcx> { ConstVal::Tuple(None, field_values) => { let repr = adt::represent_type(ccx, ty); let field_types = match ty.sty { - TyTuple(ref types) => types, + TyTuple(types) => types, _ => bug!(), }; let mut trans_fields = Vec::with_capacity(field_values.len());