Skip to content

Commit

Permalink
Rollup merge of rust-lang#41310 - eddyb:demand-const-eval, r=nikomats…
Browse files Browse the repository at this point in the history
…akis

[on-demand] Turn monomorphic_const_eval into a proper query, not just a cache.

The error definitions and reporting logic, alongside with `eval_length` were moved to `librustc`.
Both local and cross-crate constant evaluation is on-demand now, but the latter is only used for `enum` discriminants, to replace the manual insertion into the cache which was done when decoding variants.

r? @nikomatsakis
  • Loading branch information
frewsxcv authored Apr 18, 2017
2 parents b8c446e + 6dc21b7 commit 24e8158
Show file tree
Hide file tree
Showing 38 changed files with 431 additions and 448 deletions.
4 changes: 0 additions & 4 deletions src/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,25 @@ struct ListNode {
This works because `Box` is a pointer, so its size is well-known.
"##,

E0080: r##"
This error indicates that the compiler was unable to sensibly evaluate an
constant expression that had to be evaluated. Attempting to divide by 0
or causing integer overflow are two ways to induce this error. For example:
```compile_fail,E0080
enum Enum {
X = (1 << 500),
Y = (1 / 0)
}
```
Ensure that the expressions given can be evaluated as the desired integer type.
See the FFI section of the Reference for more information about using a custom
integer type:
https://doc.rust-lang.org/reference.html#ffi-attributes
"##,

E0106: r##"
This error indicates that a lifetime is missing from a type. If it is an error
inside a function signature, the problem may be with failing to adhere to the
Expand Down
199 changes: 194 additions & 5 deletions src/librustc/middle/const_val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,28 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use syntax::symbol::InternedString;
use syntax::ast;
use std::rc::Rc;
use self::ConstVal::*;
pub use rustc_const_math::ConstInt;

use hir;
use hir::def::Def;
use hir::def_id::DefId;
use ty::{self, TyCtxt};
use ty::subst::Substs;
use util::common::ErrorReported;
use rustc_const_math::*;

use self::ConstVal::*;
pub use rustc_const_math::ConstInt;
use graphviz::IntoCow;
use errors::DiagnosticBuilder;
use syntax::symbol::InternedString;
use syntax::ast;
use syntax_pos::Span;

use std::borrow::Cow;
use std::collections::BTreeMap;
use std::rc::Rc;

pub type EvalResult<'tcx> = Result<ConstVal<'tcx>, ConstEvalErr<'tcx>>;

#[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)]
pub enum ConstVal<'tcx> {
Expand Down Expand Up @@ -61,3 +72,181 @@ impl<'tcx> ConstVal<'tcx> {
}
}
}

#[derive(Clone, Debug)]
pub struct ConstEvalErr<'tcx> {
pub span: Span,
pub kind: ErrKind<'tcx>,
}

#[derive(Clone, Debug)]
pub enum ErrKind<'tcx> {
CannotCast,
MissingStructField,
NegateOn(ConstVal<'tcx>),
NotOn(ConstVal<'tcx>),
CallOn(ConstVal<'tcx>),

NonConstPath,
UnimplementedConstVal(&'static str),
ExpectedConstTuple,
ExpectedConstStruct,
IndexedNonVec,
IndexNotUsize,
IndexOutOfBounds { len: u64, index: u64 },

MiscBinaryOp,
MiscCatchAll,

IndexOpFeatureGated,
Math(ConstMathErr),

ErroneousReferencedConstant(Box<ConstEvalErr<'tcx>>),

TypeckError
}

impl<'tcx> From<ConstMathErr> for ErrKind<'tcx> {
fn from(err: ConstMathErr) -> ErrKind<'tcx> {
match err {
ConstMathErr::UnsignedNegation => ErrKind::TypeckError,
_ => ErrKind::Math(err)
}
}
}

#[derive(Clone, Debug)]
pub enum ConstEvalErrDescription<'a> {
Simple(Cow<'a, str>),
}

impl<'a> ConstEvalErrDescription<'a> {
/// Return a one-line description of the error, for lints and such
pub fn into_oneline(self) -> Cow<'a, str> {
match self {
ConstEvalErrDescription::Simple(simple) => simple,
}
}
}

impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
pub fn description(&self) -> ConstEvalErrDescription {
use self::ErrKind::*;
use self::ConstEvalErrDescription::*;

macro_rules! simple {
($msg:expr) => ({ Simple($msg.into_cow()) });
($fmt:expr, $($arg:tt)+) => ({
Simple(format!($fmt, $($arg)+).into_cow())
})
}

match self.kind {
CannotCast => simple!("can't cast this type"),
NegateOn(ref const_val) => simple!("negate on {}", const_val.description()),
NotOn(ref const_val) => simple!("not on {}", const_val.description()),
CallOn(ref const_val) => simple!("call on {}", const_val.description()),

MissingStructField => simple!("nonexistent struct field"),
NonConstPath => simple!("non-constant path in constant expression"),
UnimplementedConstVal(what) =>
simple!("unimplemented constant expression: {}", what),
ExpectedConstTuple => simple!("expected constant tuple"),
ExpectedConstStruct => simple!("expected constant struct"),
IndexedNonVec => simple!("indexing is only supported for arrays"),
IndexNotUsize => simple!("indices must be of type `usize`"),
IndexOutOfBounds { len, index } => {
simple!("index out of bounds: the len is {} but the index is {}",
len, index)
}

MiscBinaryOp => simple!("bad operands for binary"),
MiscCatchAll => simple!("unsupported constant expr"),
IndexOpFeatureGated => simple!("the index operation on const values is unstable"),
Math(ref err) => Simple(err.description().into_cow()),

ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"),

TypeckError => simple!("type-checking failed"),
}
}

pub fn struct_error(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
primary_span: Span,
primary_kind: &str)
-> DiagnosticBuilder<'gcx>
{
let mut err = self;
while let &ConstEvalErr {
kind: ErrKind::ErroneousReferencedConstant(box ref i_err), ..
} = err {
err = i_err;
}

let mut diag = struct_span_err!(tcx.sess, err.span, E0080, "constant evaluation error");
err.note(tcx, primary_span, primary_kind, &mut diag);
diag
}

pub fn note(&self,
_tcx: TyCtxt<'a, 'gcx, 'tcx>,
primary_span: Span,
primary_kind: &str,
diag: &mut DiagnosticBuilder)
{
match self.description() {
ConstEvalErrDescription::Simple(message) => {
diag.span_label(self.span, &message);
}
}

if !primary_span.contains(self.span) {
diag.span_note(primary_span,
&format!("for {} here", primary_kind));
}
}

pub fn report(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
primary_span: Span,
primary_kind: &str)
{
if let ErrKind::TypeckError = self.kind {
return;
}
self.struct_error(tcx, primary_span, primary_kind).emit();
}
}

/// Returns the value of the length-valued expression
pub fn eval_length(tcx: TyCtxt,
count: hir::BodyId,
reason: &str)
-> Result<usize, ErrorReported>
{
let count_expr = &tcx.hir.body(count).value;
let count_def_id = tcx.hir.body_owner_def_id(count);
match ty::queries::monomorphic_const_eval::get(tcx, count_expr.span, count_def_id) {
Ok(Integral(Usize(count))) => {
let val = count.as_u64(tcx.sess.target.uint_type);
assert_eq!(val as usize as u64, val);
Ok(val as usize)
},
Ok(_) |
Err(ConstEvalErr { kind: ErrKind::TypeckError, .. }) => Err(ErrorReported),
Err(err) => {
let mut diag = err.struct_error(tcx, count_expr.span, reason);

if let hir::ExprPath(hir::QPath::Resolved(None, ref path)) = count_expr.node {
if let Def::Local(..) = path.def {
diag.note(&format!("`{}` is a variable",
tcx.hir.node_to_pretty_string(count_expr.id)));
}
}

diag.emit();
Err(ErrorReported)
}
}
}
4 changes: 2 additions & 2 deletions src/librustc/ty/maps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

use dep_graph::{DepGraph, DepNode, DepTrackingMap, DepTrackingMapConfig};
use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use middle::const_val::ConstVal;
use middle::const_val;
use middle::privacy::AccessLevels;
use mir;
use session::CompileResult;
Expand Down Expand Up @@ -443,7 +443,7 @@ define_maps! { <'tcx>

/// Results of evaluating monomorphic constants embedded in
/// other items, such as enum variant explicit discriminants.
pub monomorphic_const_eval: MonomorphicConstEval(DefId) -> Result<ConstVal<'tcx>, ()>,
pub monomorphic_const_eval: MonomorphicConstEval(DefId) -> const_val::EvalResult<'tcx>,

/// Performs the privacy check and computes "access levels".
pub privacy_access_levels: PrivacyAccessLevels(CrateNum) -> Rc<AccessLevels>,
Expand Down
47 changes: 46 additions & 1 deletion src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1690,7 +1690,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
self.variants.iter().map(move |v| {
let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr());
if let VariantDiscr::Explicit(expr_did) = v.discr {
match tcx.maps.monomorphic_const_eval.borrow()[&expr_did] {
match queries::monomorphic_const_eval::get(tcx, DUMMY_SP, expr_did) {
Ok(ConstVal::Integral(v)) => {
discr = v;
}
Expand All @@ -1703,6 +1703,51 @@ impl<'a, 'gcx, 'tcx> AdtDef {
})
}

/// Compute the discriminant value used by a specific variant.
/// Unlike `discriminants`, this is (amortized) constant-time,
/// only doing at most one query for evaluating an explicit
/// discriminant (the last one before the requested variant),
/// assuming there are no constant-evaluation errors there.
pub fn discriminant_for_variant(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
variant_index: usize)
-> ConstInt {
let repr_type = self.repr.discr_type();
let mut explicit_value = repr_type.initial_discriminant(tcx.global_tcx());
let mut explicit_index = variant_index;
loop {
match self.variants[explicit_index].discr {
ty::VariantDiscr::Relative(0) => break,
ty::VariantDiscr::Relative(distance) => {
explicit_index -= distance;
}
ty::VariantDiscr::Explicit(expr_did) => {
match queries::monomorphic_const_eval::get(tcx, DUMMY_SP, expr_did) {
Ok(ConstVal::Integral(v)) => {
explicit_value = v;
break;
}
_ => {
explicit_index -= 1;
}
}
}
}
}
let discr = explicit_value.to_u128_unchecked()
.wrapping_add((variant_index - explicit_index) as u128);
match repr_type {
attr::UnsignedInt(ty) => {
ConstInt::new_unsigned_truncating(discr, ty,
tcx.sess.target.uint_type)
}
attr::SignedInt(ty) => {
ConstInt::new_signed_truncating(discr as i128, ty,
tcx.sess.target.int_type)
}
}
}

pub fn destructor(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Destructor> {
queries::adt_destructor::get(tcx, DUMMY_SP, self.did)
}
Expand Down
1 change: 0 additions & 1 deletion src/librustc_const_eval/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,4 @@ rustc_const_math = { path = "../librustc_const_math" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_errors = { path = "../librustc_errors" }
syntax = { path = "../libsyntax" }
graphviz = { path = "../libgraphviz" }
syntax_pos = { path = "../libsyntax_pos" }
Loading

0 comments on commit 24e8158

Please sign in to comment.