Skip to content

Commit 24e8158

Browse files
authored
Rollup merge of #41310 - eddyb:demand-const-eval, r=nikomatsakis
[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
2 parents b8c446e + 6dc21b7 commit 24e8158

File tree

38 files changed

+431
-448
lines changed

38 files changed

+431
-448
lines changed

src/Cargo.lock

-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/librustc/diagnostics.rs

+19
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,25 @@ struct ListNode {
327327
This works because `Box` is a pointer, so its size is well-known.
328328
"##,
329329

330+
E0080: r##"
331+
This error indicates that the compiler was unable to sensibly evaluate an
332+
constant expression that had to be evaluated. Attempting to divide by 0
333+
or causing integer overflow are two ways to induce this error. For example:
334+
335+
```compile_fail,E0080
336+
enum Enum {
337+
X = (1 << 500),
338+
Y = (1 / 0)
339+
}
340+
```
341+
342+
Ensure that the expressions given can be evaluated as the desired integer type.
343+
See the FFI section of the Reference for more information about using a custom
344+
integer type:
345+
346+
https://doc.rust-lang.org/reference.html#ffi-attributes
347+
"##,
348+
330349
E0106: r##"
331350
This error indicates that a lifetime is missing from a type. If it is an error
332351
inside a function signature, the problem may be with failing to adhere to the

src/librustc/middle/const_val.rs

+194-5
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,28 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use syntax::symbol::InternedString;
12-
use syntax::ast;
13-
use std::rc::Rc;
11+
use self::ConstVal::*;
12+
pub use rustc_const_math::ConstInt;
13+
14+
use hir;
15+
use hir::def::Def;
1416
use hir::def_id::DefId;
17+
use ty::{self, TyCtxt};
1518
use ty::subst::Substs;
19+
use util::common::ErrorReported;
1620
use rustc_const_math::*;
1721

18-
use self::ConstVal::*;
19-
pub use rustc_const_math::ConstInt;
22+
use graphviz::IntoCow;
23+
use errors::DiagnosticBuilder;
24+
use syntax::symbol::InternedString;
25+
use syntax::ast;
26+
use syntax_pos::Span;
2027

28+
use std::borrow::Cow;
2129
use std::collections::BTreeMap;
30+
use std::rc::Rc;
31+
32+
pub type EvalResult<'tcx> = Result<ConstVal<'tcx>, ConstEvalErr<'tcx>>;
2233

2334
#[derive(Clone, Debug, Hash, RustcEncodable, RustcDecodable, Eq, PartialEq)]
2435
pub enum ConstVal<'tcx> {
@@ -61,3 +72,181 @@ impl<'tcx> ConstVal<'tcx> {
6172
}
6273
}
6374
}
75+
76+
#[derive(Clone, Debug)]
77+
pub struct ConstEvalErr<'tcx> {
78+
pub span: Span,
79+
pub kind: ErrKind<'tcx>,
80+
}
81+
82+
#[derive(Clone, Debug)]
83+
pub enum ErrKind<'tcx> {
84+
CannotCast,
85+
MissingStructField,
86+
NegateOn(ConstVal<'tcx>),
87+
NotOn(ConstVal<'tcx>),
88+
CallOn(ConstVal<'tcx>),
89+
90+
NonConstPath,
91+
UnimplementedConstVal(&'static str),
92+
ExpectedConstTuple,
93+
ExpectedConstStruct,
94+
IndexedNonVec,
95+
IndexNotUsize,
96+
IndexOutOfBounds { len: u64, index: u64 },
97+
98+
MiscBinaryOp,
99+
MiscCatchAll,
100+
101+
IndexOpFeatureGated,
102+
Math(ConstMathErr),
103+
104+
ErroneousReferencedConstant(Box<ConstEvalErr<'tcx>>),
105+
106+
TypeckError
107+
}
108+
109+
impl<'tcx> From<ConstMathErr> for ErrKind<'tcx> {
110+
fn from(err: ConstMathErr) -> ErrKind<'tcx> {
111+
match err {
112+
ConstMathErr::UnsignedNegation => ErrKind::TypeckError,
113+
_ => ErrKind::Math(err)
114+
}
115+
}
116+
}
117+
118+
#[derive(Clone, Debug)]
119+
pub enum ConstEvalErrDescription<'a> {
120+
Simple(Cow<'a, str>),
121+
}
122+
123+
impl<'a> ConstEvalErrDescription<'a> {
124+
/// Return a one-line description of the error, for lints and such
125+
pub fn into_oneline(self) -> Cow<'a, str> {
126+
match self {
127+
ConstEvalErrDescription::Simple(simple) => simple,
128+
}
129+
}
130+
}
131+
132+
impl<'a, 'gcx, 'tcx> ConstEvalErr<'tcx> {
133+
pub fn description(&self) -> ConstEvalErrDescription {
134+
use self::ErrKind::*;
135+
use self::ConstEvalErrDescription::*;
136+
137+
macro_rules! simple {
138+
($msg:expr) => ({ Simple($msg.into_cow()) });
139+
($fmt:expr, $($arg:tt)+) => ({
140+
Simple(format!($fmt, $($arg)+).into_cow())
141+
})
142+
}
143+
144+
match self.kind {
145+
CannotCast => simple!("can't cast this type"),
146+
NegateOn(ref const_val) => simple!("negate on {}", const_val.description()),
147+
NotOn(ref const_val) => simple!("not on {}", const_val.description()),
148+
CallOn(ref const_val) => simple!("call on {}", const_val.description()),
149+
150+
MissingStructField => simple!("nonexistent struct field"),
151+
NonConstPath => simple!("non-constant path in constant expression"),
152+
UnimplementedConstVal(what) =>
153+
simple!("unimplemented constant expression: {}", what),
154+
ExpectedConstTuple => simple!("expected constant tuple"),
155+
ExpectedConstStruct => simple!("expected constant struct"),
156+
IndexedNonVec => simple!("indexing is only supported for arrays"),
157+
IndexNotUsize => simple!("indices must be of type `usize`"),
158+
IndexOutOfBounds { len, index } => {
159+
simple!("index out of bounds: the len is {} but the index is {}",
160+
len, index)
161+
}
162+
163+
MiscBinaryOp => simple!("bad operands for binary"),
164+
MiscCatchAll => simple!("unsupported constant expr"),
165+
IndexOpFeatureGated => simple!("the index operation on const values is unstable"),
166+
Math(ref err) => Simple(err.description().into_cow()),
167+
168+
ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"),
169+
170+
TypeckError => simple!("type-checking failed"),
171+
}
172+
}
173+
174+
pub fn struct_error(&self,
175+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
176+
primary_span: Span,
177+
primary_kind: &str)
178+
-> DiagnosticBuilder<'gcx>
179+
{
180+
let mut err = self;
181+
while let &ConstEvalErr {
182+
kind: ErrKind::ErroneousReferencedConstant(box ref i_err), ..
183+
} = err {
184+
err = i_err;
185+
}
186+
187+
let mut diag = struct_span_err!(tcx.sess, err.span, E0080, "constant evaluation error");
188+
err.note(tcx, primary_span, primary_kind, &mut diag);
189+
diag
190+
}
191+
192+
pub fn note(&self,
193+
_tcx: TyCtxt<'a, 'gcx, 'tcx>,
194+
primary_span: Span,
195+
primary_kind: &str,
196+
diag: &mut DiagnosticBuilder)
197+
{
198+
match self.description() {
199+
ConstEvalErrDescription::Simple(message) => {
200+
diag.span_label(self.span, &message);
201+
}
202+
}
203+
204+
if !primary_span.contains(self.span) {
205+
diag.span_note(primary_span,
206+
&format!("for {} here", primary_kind));
207+
}
208+
}
209+
210+
pub fn report(&self,
211+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
212+
primary_span: Span,
213+
primary_kind: &str)
214+
{
215+
if let ErrKind::TypeckError = self.kind {
216+
return;
217+
}
218+
self.struct_error(tcx, primary_span, primary_kind).emit();
219+
}
220+
}
221+
222+
/// Returns the value of the length-valued expression
223+
pub fn eval_length(tcx: TyCtxt,
224+
count: hir::BodyId,
225+
reason: &str)
226+
-> Result<usize, ErrorReported>
227+
{
228+
let count_expr = &tcx.hir.body(count).value;
229+
let count_def_id = tcx.hir.body_owner_def_id(count);
230+
match ty::queries::monomorphic_const_eval::get(tcx, count_expr.span, count_def_id) {
231+
Ok(Integral(Usize(count))) => {
232+
let val = count.as_u64(tcx.sess.target.uint_type);
233+
assert_eq!(val as usize as u64, val);
234+
Ok(val as usize)
235+
},
236+
Ok(_) |
237+
Err(ConstEvalErr { kind: ErrKind::TypeckError, .. }) => Err(ErrorReported),
238+
Err(err) => {
239+
let mut diag = err.struct_error(tcx, count_expr.span, reason);
240+
241+
if let hir::ExprPath(hir::QPath::Resolved(None, ref path)) = count_expr.node {
242+
if let Def::Local(..) = path.def {
243+
diag.note(&format!("`{}` is a variable",
244+
tcx.hir.node_to_pretty_string(count_expr.id)));
245+
}
246+
}
247+
248+
diag.emit();
249+
Err(ErrorReported)
250+
}
251+
}
252+
}

src/librustc/ty/maps.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use dep_graph::{DepGraph, DepNode, DepTrackingMap, DepTrackingMapConfig};
1212
use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
13-
use middle::const_val::ConstVal;
13+
use middle::const_val;
1414
use middle::privacy::AccessLevels;
1515
use mir;
1616
use session::CompileResult;
@@ -443,7 +443,7 @@ define_maps! { <'tcx>
443443

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

448448
/// Performs the privacy check and computes "access levels".
449449
pub privacy_access_levels: PrivacyAccessLevels(CrateNum) -> Rc<AccessLevels>,

src/librustc/ty/mod.rs

+46-1
Original file line numberDiff line numberDiff line change
@@ -1690,7 +1690,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
16901690
self.variants.iter().map(move |v| {
16911691
let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr());
16921692
if let VariantDiscr::Explicit(expr_did) = v.discr {
1693-
match tcx.maps.monomorphic_const_eval.borrow()[&expr_did] {
1693+
match queries::monomorphic_const_eval::get(tcx, DUMMY_SP, expr_did) {
16941694
Ok(ConstVal::Integral(v)) => {
16951695
discr = v;
16961696
}
@@ -1703,6 +1703,51 @@ impl<'a, 'gcx, 'tcx> AdtDef {
17031703
})
17041704
}
17051705

1706+
/// Compute the discriminant value used by a specific variant.
1707+
/// Unlike `discriminants`, this is (amortized) constant-time,
1708+
/// only doing at most one query for evaluating an explicit
1709+
/// discriminant (the last one before the requested variant),
1710+
/// assuming there are no constant-evaluation errors there.
1711+
pub fn discriminant_for_variant(&self,
1712+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
1713+
variant_index: usize)
1714+
-> ConstInt {
1715+
let repr_type = self.repr.discr_type();
1716+
let mut explicit_value = repr_type.initial_discriminant(tcx.global_tcx());
1717+
let mut explicit_index = variant_index;
1718+
loop {
1719+
match self.variants[explicit_index].discr {
1720+
ty::VariantDiscr::Relative(0) => break,
1721+
ty::VariantDiscr::Relative(distance) => {
1722+
explicit_index -= distance;
1723+
}
1724+
ty::VariantDiscr::Explicit(expr_did) => {
1725+
match queries::monomorphic_const_eval::get(tcx, DUMMY_SP, expr_did) {
1726+
Ok(ConstVal::Integral(v)) => {
1727+
explicit_value = v;
1728+
break;
1729+
}
1730+
_ => {
1731+
explicit_index -= 1;
1732+
}
1733+
}
1734+
}
1735+
}
1736+
}
1737+
let discr = explicit_value.to_u128_unchecked()
1738+
.wrapping_add((variant_index - explicit_index) as u128);
1739+
match repr_type {
1740+
attr::UnsignedInt(ty) => {
1741+
ConstInt::new_unsigned_truncating(discr, ty,
1742+
tcx.sess.target.uint_type)
1743+
}
1744+
attr::SignedInt(ty) => {
1745+
ConstInt::new_signed_truncating(discr as i128, ty,
1746+
tcx.sess.target.int_type)
1747+
}
1748+
}
1749+
}
1750+
17061751
pub fn destructor(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Destructor> {
17071752
queries::adt_destructor::get(tcx, DUMMY_SP, self.did)
17081753
}

src/librustc_const_eval/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,4 @@ rustc_const_math = { path = "../librustc_const_math" }
1717
rustc_data_structures = { path = "../librustc_data_structures" }
1818
rustc_errors = { path = "../librustc_errors" }
1919
syntax = { path = "../libsyntax" }
20-
graphviz = { path = "../libgraphviz" }
2120
syntax_pos = { path = "../libsyntax_pos" }

0 commit comments

Comments
 (0)