Skip to content

Commit 2d67edd

Browse files
committed
Move const eval query components into their own module
1 parent 3e7fa3c commit 2d67edd

File tree

3 files changed

+234
-228
lines changed

3 files changed

+234
-228
lines changed

src/librustc_mir/const_eval.rs

Lines changed: 4 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,8 @@ use std::error::Error;
55
use std::fmt;
66
use std::hash::Hash;
77

8-
use crate::interpret::eval_nullary_intrinsic;
9-
use crate::interpret::eval_nullary_intrinsic;
10-
use rustc::hir::def::DefKind;
118
use rustc::mir;
12-
use rustc::mir::interpret::{ConstEvalErr, ErrorHandled, ScalarMaybeUndef};
13-
use rustc::traits::Reveal;
9+
use rustc::mir::interpret::ScalarMaybeUndef;
1410
use rustc::ty::layout::{self, LayoutOf, VariantIdx};
1511
use rustc::ty::{self, subst::Subst, TyCtxt};
1612

@@ -21,15 +17,14 @@ use syntax::{
2117

2218
use crate::interpret::{
2319
intern_const_alloc_recursive, Allocation, ConstValue, GlobalId, ImmTy, Immediate, InterpCx,
24-
InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemoryKind, OpTy, RawConst, RefTracking,
25-
Scalar, StackPopCleanup,
20+
InterpResult, MPlaceTy, MemoryKind, OpTy, Scalar, StackPopCleanup,
2621
};
2722

2823
mod error;
29-
mod machine;
24+
mod query;
3025

3126
pub use error::*;
32-
pub use machine::*;
27+
pub use query::*;
3328

3429
/// The `InterpCx` is only meant to be used to do field and index projections into constants for
3530
/// `simd_shuffle` and const patterns in match arms.
@@ -225,220 +220,3 @@ pub fn const_variant_index<'tcx>(
225220
let op = ecx.eval_const_to_op(val, None).unwrap();
226221
ecx.read_discriminant(op).unwrap().1
227222
}
228-
229-
/// Turn an interpreter error into something to report to the user.
230-
/// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
231-
/// Should be called only if the error is actually going to to be reported!
232-
pub fn error_to_const_error<'mir, 'tcx, M: Machine<'mir, 'tcx>>(
233-
ecx: &InterpCx<'mir, 'tcx, M>,
234-
mut error: InterpErrorInfo<'tcx>,
235-
) -> ConstEvalErr<'tcx> {
236-
error.print_backtrace();
237-
let stacktrace = ecx.generate_stacktrace(None);
238-
ConstEvalErr { error: error.kind, stacktrace, span: ecx.tcx.span }
239-
}
240-
241-
pub fn note_on_undefined_behavior_error() -> &'static str {
242-
"The rules on what exactly is undefined behavior aren't clear, \
243-
so this check might be overzealous. Please open an issue on the rustc \
244-
repository if you believe it should not be considered undefined behavior."
245-
}
246-
247-
fn validate_and_turn_into_const<'tcx>(
248-
tcx: TyCtxt<'tcx>,
249-
constant: RawConst<'tcx>,
250-
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
251-
) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
252-
let cid = key.value;
253-
let def_id = cid.instance.def.def_id();
254-
let is_static = tcx.is_static(def_id);
255-
let ecx = mk_eval_cx(tcx, tcx.def_span(key.value.instance.def_id()), key.param_env, is_static);
256-
let val = (|| {
257-
let mplace = ecx.raw_const_to_mplace(constant)?;
258-
let mut ref_tracking = RefTracking::new(mplace);
259-
while let Some((mplace, path)) = ref_tracking.todo.pop() {
260-
ecx.validate_operand(mplace.into(), path, Some(&mut ref_tracking))?;
261-
}
262-
// Now that we validated, turn this into a proper constant.
263-
// Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
264-
// whether they become immediates.
265-
if is_static || cid.promoted.is_some() {
266-
let ptr = mplace.ptr.to_ptr()?;
267-
Ok(tcx.mk_const(ty::Const {
268-
val: ty::ConstKind::Value(ConstValue::ByRef {
269-
alloc: ecx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id),
270-
offset: ptr.offset,
271-
}),
272-
ty: mplace.layout.ty,
273-
}))
274-
} else {
275-
Ok(op_to_const(&ecx, mplace.into()))
276-
}
277-
})();
278-
279-
val.map_err(|error| {
280-
let err = error_to_const_error(&ecx, error);
281-
match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
282-
Ok(mut diag) => {
283-
diag.note(note_on_undefined_behavior_error());
284-
diag.emit();
285-
ErrorHandled::Reported
286-
}
287-
Err(err) => err,
288-
}
289-
})
290-
}
291-
292-
pub fn const_eval_validated_provider<'tcx>(
293-
tcx: TyCtxt<'tcx>,
294-
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
295-
) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
296-
// see comment in const_eval_raw_provider for what we're doing here
297-
if key.param_env.reveal == Reveal::All {
298-
let mut key = key.clone();
299-
key.param_env.reveal = Reveal::UserFacing;
300-
match tcx.const_eval_validated(key) {
301-
// try again with reveal all as requested
302-
Err(ErrorHandled::TooGeneric) => {
303-
// Promoteds should never be "too generic" when getting evaluated.
304-
// They either don't get evaluated, or we are in a monomorphic context
305-
assert!(key.value.promoted.is_none());
306-
}
307-
// dedupliate calls
308-
other => return other,
309-
}
310-
}
311-
312-
// We call `const_eval` for zero arg intrinsics, too, in order to cache their value.
313-
// Catch such calls and evaluate them instead of trying to load a constant's MIR.
314-
if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def {
315-
let ty = key.value.instance.ty(tcx);
316-
let substs = match ty.kind {
317-
ty::FnDef(_, substs) => substs,
318-
_ => bug!("intrinsic with type {:?}", ty),
319-
};
320-
return eval_nullary_intrinsic(tcx, key.param_env, def_id, substs).map_err(|error| {
321-
let span = tcx.def_span(def_id);
322-
let error = ConstEvalErr { error: error.kind, stacktrace: vec![], span };
323-
error.report_as_error(tcx.at(span), "could not evaluate nullary intrinsic")
324-
});
325-
}
326-
327-
tcx.const_eval_raw(key).and_then(|val| validate_and_turn_into_const(tcx, val, key))
328-
}
329-
330-
pub fn const_eval_raw_provider<'tcx>(
331-
tcx: TyCtxt<'tcx>,
332-
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
333-
) -> ::rustc::mir::interpret::ConstEvalRawResult<'tcx> {
334-
// Because the constant is computed twice (once per value of `Reveal`), we are at risk of
335-
// reporting the same error twice here. To resolve this, we check whether we can evaluate the
336-
// constant in the more restrictive `Reveal::UserFacing`, which most likely already was
337-
// computed. For a large percentage of constants that will already have succeeded. Only
338-
// associated constants of generic functions will fail due to not enough monomorphization
339-
// information being available.
340-
341-
// In case we fail in the `UserFacing` variant, we just do the real computation.
342-
if key.param_env.reveal == Reveal::All {
343-
let mut key = key.clone();
344-
key.param_env.reveal = Reveal::UserFacing;
345-
match tcx.const_eval_raw(key) {
346-
// try again with reveal all as requested
347-
Err(ErrorHandled::TooGeneric) => {}
348-
// dedupliate calls
349-
other => return other,
350-
}
351-
}
352-
if cfg!(debug_assertions) {
353-
// Make sure we format the instance even if we do not print it.
354-
// This serves as a regression test against an ICE on printing.
355-
// The next two lines concatenated contain some discussion:
356-
// https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/
357-
// subject/anon_const_instance_printing/near/135980032
358-
let instance = key.value.instance.to_string();
359-
trace!("const eval: {:?} ({})", key, instance);
360-
}
361-
362-
let cid = key.value;
363-
let def_id = cid.instance.def.def_id();
364-
365-
if def_id.is_local() && tcx.typeck_tables_of(def_id).tainted_by_errors {
366-
return Err(ErrorHandled::Reported);
367-
}
368-
369-
let is_static = tcx.is_static(def_id);
370-
371-
let span = tcx.def_span(cid.instance.def_id());
372-
let mut ecx = InterpCx::new(
373-
tcx.at(span),
374-
key.param_env,
375-
CompileTimeInterpreter::new(),
376-
MemoryExtra { can_access_statics: is_static },
377-
);
378-
379-
let res = ecx.load_mir(cid.instance.def, cid.promoted);
380-
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, *body))
381-
.and_then(|place| {
382-
Ok(RawConst { alloc_id: place.ptr.assert_ptr().alloc_id, ty: place.layout.ty })
383-
})
384-
.map_err(|error| {
385-
let err = error_to_const_error(&ecx, error);
386-
// errors in statics are always emitted as fatal errors
387-
if is_static {
388-
// Ensure that if the above error was either `TooGeneric` or `Reported`
389-
// an error must be reported.
390-
let v = err.report_as_error(ecx.tcx, "could not evaluate static initializer");
391-
tcx.sess.delay_span_bug(
392-
err.span,
393-
&format!("static eval failure did not emit an error: {:#?}", v),
394-
);
395-
v
396-
} else if def_id.is_local() {
397-
// constant defined in this crate, we can figure out a lint level!
398-
match tcx.def_kind(def_id) {
399-
// constants never produce a hard error at the definition site. Anything else is
400-
// a backwards compatibility hazard (and will break old versions of winapi for sure)
401-
//
402-
// note that validation may still cause a hard error on this very same constant,
403-
// because any code that existed before validation could not have failed validation
404-
// thus preventing such a hard error from being a backwards compatibility hazard
405-
Some(DefKind::Const) | Some(DefKind::AssocConst) => {
406-
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
407-
err.report_as_lint(
408-
tcx.at(tcx.def_span(def_id)),
409-
"any use of this value will cause an error",
410-
hir_id,
411-
Some(err.span),
412-
)
413-
}
414-
// promoting runtime code is only allowed to error if it references broken constants
415-
// any other kind of error will be reported to the user as a deny-by-default lint
416-
_ => {
417-
if let Some(p) = cid.promoted {
418-
let span = tcx.promoted_mir(def_id)[p].span;
419-
if let err_inval!(ReferencedConstant) = err.error {
420-
err.report_as_error(
421-
tcx.at(span),
422-
"evaluation of constant expression failed",
423-
)
424-
} else {
425-
err.report_as_lint(
426-
tcx.at(span),
427-
"reaching this expression at runtime will panic or abort",
428-
tcx.hir().as_local_hir_id(def_id).unwrap(),
429-
Some(err.span),
430-
)
431-
}
432-
// anything else (array lengths, enum initializers, constant patterns) are reported
433-
// as hard errors
434-
} else {
435-
err.report_as_error(ecx.tcx, "evaluation of constant value failed")
436-
}
437-
}
438-
}
439-
} else {
440-
// use of broken constant from other crate
441-
err.report_as_error(ecx.tcx, "could not evaluate constant")
442-
}
443-
})
444-
}

src/librustc_mir/const_eval/error.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use std::error::Error;
22
use std::fmt;
33

4-
use crate::interpret::InterpErrorInfo;
5-
4+
use super::InterpCx;
5+
use crate::interpret::{ConstEvalErr, InterpErrorInfo, Machine};
66
#[derive(Clone, Debug)]
77
pub enum ConstEvalError {
88
NeedsRfc(String),
@@ -28,3 +28,15 @@ impl fmt::Display for ConstEvalError {
2828
}
2929

3030
impl Error for ConstEvalError {}
31+
32+
/// Turn an interpreter error into something to report to the user.
33+
/// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
34+
/// Should be called only if the error is actually going to to be reported!
35+
pub fn error_to_const_error<'mir, 'tcx, M: Machine<'mir, 'tcx>>(
36+
ecx: &InterpCx<'mir, 'tcx, M>,
37+
mut error: InterpErrorInfo<'tcx>,
38+
) -> ConstEvalErr<'tcx> {
39+
error.print_backtrace();
40+
let stacktrace = ecx.generate_stacktrace(None);
41+
ConstEvalErr { error: error.kind, stacktrace, span: ecx.tcx.span }
42+
}

0 commit comments

Comments
 (0)