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

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

Merged
merged 3 commits into from
Apr 18, 2017
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
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 @@ -1660,7 +1660,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 @@ -1673,6 +1673,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