Skip to content

Commit b4dffc9

Browse files
authored
Rollup merge of #122397 - oli-obk:machine-read-hook2, r=RalfJung
Various cleanups around the const eval query providers r? `@RalfJung` after this, working on running validation before interning starts with swapping the order of two lines of code
2 parents 1dce191 + a316c21 commit b4dffc9

File tree

10 files changed

+133
-146
lines changed

10 files changed

+133
-146
lines changed

compiler/rustc_const_eval/messages.ftl

+6-6
Original file line numberDiff line numberDiff line change
@@ -374,12 +374,6 @@ const_eval_unallowed_op_in_const_context =
374374
const_eval_unavailable_target_features_for_fn =
375375
calling a function that requires unavailable target features: {$unavailable_feats}
376376
377-
const_eval_undefined_behavior =
378-
it is undefined behavior to use this value
379-
380-
const_eval_undefined_behavior_note =
381-
The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
382-
383377
const_eval_uninhabited_enum_variant_read =
384378
read discriminant of an uninhabited enum variant
385379
const_eval_uninhabited_enum_variant_written =
@@ -434,6 +428,12 @@ const_eval_validation_expected_raw_ptr = expected a raw pointer
434428
const_eval_validation_expected_ref = expected a reference
435429
const_eval_validation_expected_str = expected a string
436430
431+
const_eval_validation_failure =
432+
it is undefined behavior to use this value
433+
434+
const_eval_validation_failure_note =
435+
The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
436+
437437
const_eval_validation_front_matter_invalid_value = constructing invalid value
438438
const_eval_validation_front_matter_invalid_value_with_path = constructing invalid value at {$path}
439439

compiler/rustc_const_eval/src/const_eval/error.rs

+6-8
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@ use std::mem;
22

33
use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, Diagnostic, IntoDiagArg};
44
use rustc_hir::CRATE_HIR_ID;
5+
use rustc_middle::mir::interpret::Provenance;
56
use rustc_middle::mir::AssertKind;
67
use rustc_middle::query::TyCtxtAt;
78
use rustc_middle::ty::TyCtxt;
89
use rustc_middle::ty::{layout::LayoutError, ConstInt};
910
use rustc_span::{Span, Symbol, DUMMY_SP};
1011

11-
use super::{CompileTimeInterpreter, InterpCx};
12+
use super::CompileTimeInterpreter;
1213
use crate::errors::{self, FrameNote, ReportErrorExt};
13-
use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, MachineStopType};
14+
use crate::interpret::{ErrorHandled, Frame, InterpError, InterpErrorInfo, MachineStopType};
1415

1516
/// The CTFE machine has some custom error kinds.
1617
#[derive(Clone, Debug)]
@@ -58,15 +59,12 @@ impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
5859

5960
pub fn get_span_and_frames<'tcx, 'mir>(
6061
tcx: TyCtxtAt<'tcx>,
61-
machine: &CompileTimeInterpreter<'mir, 'tcx>,
62+
stack: &[Frame<'mir, 'tcx, impl Provenance, impl Sized>],
6263
) -> (Span, Vec<errors::FrameNote>)
6364
where
6465
'tcx: 'mir,
6566
{
66-
let mut stacktrace =
67-
InterpCx::<CompileTimeInterpreter<'mir, 'tcx>>::generate_stacktrace_from_stack(
68-
&machine.stack,
69-
);
67+
let mut stacktrace = Frame::generate_stacktrace_from_stack(stack);
7068
// Filter out `requires_caller_location` frames.
7169
stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx));
7270
let span = stacktrace.first().map(|f| f.span).unwrap_or(tcx.span);
@@ -170,7 +168,7 @@ pub(super) fn lint<'tcx, 'mir, L>(
170168
) where
171169
L: for<'a> rustc_errors::LintDiagnostic<'a, ()>,
172170
{
173-
let (span, frames) = get_span_and_frames(tcx, machine);
171+
let (span, frames) = get_span_and_frames(tcx, &machine.stack);
174172

175173
tcx.emit_node_span_lint(
176174
lint,

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

+74-88
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@ use crate::errors;
1818
use crate::errors::ConstEvalError;
1919
use crate::interpret::eval_nullary_intrinsic;
2020
use crate::interpret::{
21-
create_static_alloc, intern_const_alloc_recursive, take_static_root_alloc, CtfeValidationMode,
22-
GlobalId, Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind,
23-
OpTy, RefTracking, StackPopCleanup,
21+
create_static_alloc, intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate,
22+
InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking,
23+
StackPopCleanup,
2424
};
2525

2626
// Returns a pointer to where the result lives
27-
#[instrument(level = "trace", skip(ecx, body), ret)]
28-
fn eval_body_using_ecx<'mir, 'tcx>(
27+
#[instrument(level = "trace", skip(ecx, body))]
28+
fn eval_body_using_ecx<'mir, 'tcx, R: InterpretationResult<'tcx>>(
2929
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
3030
cid: GlobalId<'tcx>,
3131
body: &'mir mir::Body<'tcx>,
32-
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
32+
) -> InterpResult<'tcx, R> {
3333
trace!(?ecx.param_env);
3434
let tcx = *ecx.tcx;
3535
assert!(
@@ -84,7 +84,10 @@ fn eval_body_using_ecx<'mir, 'tcx>(
8484
// Intern the result
8585
intern_const_alloc_recursive(ecx, intern_kind, &ret)?;
8686

87-
Ok(ret)
87+
// Since evaluation had no errors, validate the resulting constant.
88+
const_validate_mplace(&ecx, &ret, cid)?;
89+
90+
Ok(R::make_result(ret, ecx))
8891
}
8992

9093
/// The `InterpCx` is only meant to be used to do field and index projections into constants for
@@ -282,18 +285,26 @@ pub fn eval_static_initializer_provider<'tcx>(
282285

283286
let instance = ty::Instance::mono(tcx, def_id.to_def_id());
284287
let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None };
285-
let mut ecx = InterpCx::new(
286-
tcx,
287-
tcx.def_span(def_id),
288-
ty::ParamEnv::reveal_all(),
289-
// Statics (and promoteds inside statics) may access other statics, because unlike consts
290-
// they do not have to behave "as if" they were evaluated at runtime.
291-
CompileTimeInterpreter::new(CanAccessMutGlobal::Yes, CheckAlignment::Error),
292-
);
293-
let alloc_id = eval_in_interpreter(&mut ecx, cid, true)?.alloc_id;
294-
let alloc = take_static_root_alloc(&mut ecx, alloc_id);
295-
let alloc = tcx.mk_const_alloc(alloc);
296-
Ok(alloc)
288+
eval_in_interpreter(tcx, cid, ty::ParamEnv::reveal_all())
289+
}
290+
291+
pub trait InterpretationResult<'tcx> {
292+
/// This function takes the place where the result of the evaluation is stored
293+
/// and prepares it for returning it in the appropriate format needed by the specific
294+
/// evaluation query.
295+
fn make_result<'mir>(
296+
mplace: MPlaceTy<'tcx>,
297+
ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
298+
) -> Self;
299+
}
300+
301+
impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> {
302+
fn make_result<'mir>(
303+
mplace: MPlaceTy<'tcx>,
304+
_ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
305+
) -> Self {
306+
ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty }
307+
}
297308
}
298309

299310
#[instrument(skip(tcx), level = "debug")]
@@ -319,92 +330,64 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
319330
trace!("const eval: {:?} ({})", key, instance);
320331
}
321332

322-
let cid = key.value;
333+
eval_in_interpreter(tcx, key.value, key.param_env)
334+
}
335+
336+
fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
337+
tcx: TyCtxt<'tcx>,
338+
cid: GlobalId<'tcx>,
339+
param_env: ty::ParamEnv<'tcx>,
340+
) -> Result<R, ErrorHandled> {
323341
let def = cid.instance.def.def_id();
324342
let is_static = tcx.is_static(def);
325343

326344
let mut ecx = InterpCx::new(
327345
tcx,
328346
tcx.def_span(def),
329-
key.param_env,
347+
param_env,
330348
// Statics (and promoteds inside statics) may access mutable global memory, because unlike consts
331349
// they do not have to behave "as if" they were evaluated at runtime.
332350
// For consts however we want to ensure they behave "as if" they were evaluated at runtime,
333351
// so we have to reject reading mutable global memory.
334352
CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
335353
);
336-
eval_in_interpreter(&mut ecx, cid, is_static)
337-
}
338-
339-
pub fn eval_in_interpreter<'mir, 'tcx>(
340-
ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
341-
cid: GlobalId<'tcx>,
342-
is_static: bool,
343-
) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
344-
// `is_static` just means "in static", it could still be a promoted!
345-
debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some());
346-
347354
let res = ecx.load_mir(cid.instance.def, cid.promoted);
348-
match res.and_then(|body| eval_body_using_ecx(ecx, cid, body)) {
349-
Err(error) => {
350-
let (error, backtrace) = error.into_parts();
351-
backtrace.print_backtrace();
352-
353-
let (kind, instance) = if is_static {
354-
("static", String::new())
355-
} else {
356-
// If the current item has generics, we'd like to enrich the message with the
357-
// instance and its args: to show the actual compile-time values, in addition to
358-
// the expression, leading to the const eval error.
359-
let instance = &cid.instance;
360-
if !instance.args.is_empty() {
361-
let instance = with_no_trimmed_paths!(instance.to_string());
362-
("const_with_path", instance)
363-
} else {
364-
("const", String::new())
365-
}
366-
};
367-
368-
Err(super::report(
369-
*ecx.tcx,
370-
error,
371-
None,
372-
|| super::get_span_and_frames(ecx.tcx, &ecx.machine),
373-
|span, frames| ConstEvalError {
374-
span,
375-
error_kind: kind,
376-
instance,
377-
frame_notes: frames,
378-
},
379-
))
380-
}
381-
Ok(mplace) => {
382-
// Since evaluation had no errors, validate the resulting constant.
383-
384-
// Temporarily allow access to the static_root_ids for the purpose of validation.
385-
let static_root_ids = ecx.machine.static_root_ids.take();
386-
let res = const_validate_mplace(&ecx, &mplace, cid);
387-
ecx.machine.static_root_ids = static_root_ids;
388-
389-
let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
390-
391-
// Validation failed, report an error.
392-
if let Err(error) = res {
393-
Err(const_report_error(&ecx, error, alloc_id))
355+
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)).map_err(|error| {
356+
let (error, backtrace) = error.into_parts();
357+
backtrace.print_backtrace();
358+
359+
let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) {
360+
("static", String::new())
361+
} else {
362+
// If the current item has generics, we'd like to enrich the message with the
363+
// instance and its args: to show the actual compile-time values, in addition to
364+
// the expression, leading to the const eval error.
365+
let instance = &cid.instance;
366+
if !instance.args.is_empty() {
367+
let instance = with_no_trimmed_paths!(instance.to_string());
368+
("const_with_path", instance)
394369
} else {
395-
// Convert to raw constant
396-
Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty })
370+
("const", String::new())
397371
}
398-
}
399-
}
372+
};
373+
374+
super::report(
375+
*ecx.tcx,
376+
error,
377+
None,
378+
|| super::get_span_and_frames(ecx.tcx, ecx.stack()),
379+
|span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames },
380+
)
381+
})
400382
}
401383

402384
#[inline(always)]
403-
pub fn const_validate_mplace<'mir, 'tcx>(
385+
fn const_validate_mplace<'mir, 'tcx>(
404386
ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
405387
mplace: &MPlaceTy<'tcx>,
406388
cid: GlobalId<'tcx>,
407-
) -> InterpResult<'tcx> {
389+
) -> Result<(), ErrorHandled> {
390+
let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
408391
let mut ref_tracking = RefTracking::new(mplace.clone());
409392
let mut inner = false;
410393
while let Some((mplace, path)) = ref_tracking.todo.pop() {
@@ -418,15 +401,18 @@ pub fn const_validate_mplace<'mir, 'tcx>(
418401
CtfeValidationMode::Const { allow_immutable_unsafe_cell: !inner }
419402
}
420403
};
421-
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?;
404+
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)
405+
// Instead of just reporting the `InterpError` via the usual machinery, we give a more targetted
406+
// error about the validation failure.
407+
.map_err(|error| report_validation_error(&ecx, error, alloc_id))?;
422408
inner = true;
423409
}
424410

425411
Ok(())
426412
}
427413

428414
#[inline(always)]
429-
pub fn const_report_error<'mir, 'tcx>(
415+
fn report_validation_error<'mir, 'tcx>(
430416
ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
431417
error: InterpErrorInfo<'tcx>,
432418
alloc_id: AllocId,
@@ -444,7 +430,7 @@ pub fn const_report_error<'mir, 'tcx>(
444430
*ecx.tcx,
445431
error,
446432
None,
447-
|| crate::const_eval::get_span_and_frames(ecx.tcx, &ecx.machine),
448-
move |span, frames| errors::UndefinedBehavior { span, ub_note, frames, raw_bytes },
433+
|| crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()),
434+
move |span, frames| errors::ValidationFailure { span, ub_note, frames, raw_bytes },
449435
)
450436
}

compiler/rustc_const_eval/src/const_eval/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_middle::mir::interpret::InterpErrorInfo;
55
use rustc_middle::query::TyCtxtAt;
66
use rustc_middle::ty::{self, Ty};
77

8-
use crate::interpret::{format_interp_error, InterpCx};
8+
use crate::interpret::format_interp_error;
99

1010
mod error;
1111
mod eval_queries;

compiler/rustc_const_eval/src/errors.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -412,11 +412,11 @@ pub struct NullaryIntrinsicError {
412412
}
413413

414414
#[derive(Diagnostic)]
415-
#[diag(const_eval_undefined_behavior, code = E0080)]
416-
pub struct UndefinedBehavior {
415+
#[diag(const_eval_validation_failure, code = E0080)]
416+
pub struct ValidationFailure {
417417
#[primary_span]
418418
pub span: Span,
419-
#[note(const_eval_undefined_behavior_note)]
419+
#[note(const_eval_validation_failure_note)]
420420
pub ub_note: Option<()>,
421421
#[subdiagnostic]
422422
pub frames: Vec<FrameNote>,

0 commit comments

Comments
 (0)