Skip to content

Commit

Permalink
Auto merge of rust-lang#122466 - matthiaskrgr:rollup-hh9mjj4, r=matth…
Browse files Browse the repository at this point in the history
…iaskrgr

Rollup of 11 pull requests

Successful merges:

 - rust-lang#104353 (Add CStr::bytes iterator)
 - rust-lang#114038 (unix time module now return result)
 - rust-lang#119676 (rustdoc-search: search types by higher-order functions)
 - rust-lang#120699 (Document `TRACK_DIAGNOSTIC` calls.)
 - rust-lang#121899 (Document how removing a type's field can be bad and what to do instead)
 - rust-lang#121940 (Mention Register Size in `#[warn(asm_sub_register)]`)
 - rust-lang#122397 (Various cleanups around the const eval query providers)
 - rust-lang#122405 (Add methods to create StableMIR constant)
 - rust-lang#122416 (Various style improvements to `rustc_lint::levels`)
 - rust-lang#122440 (const-eval: organize and extend tests for required-consts)
 - rust-lang#122461 (fix unsoundness in Step::forward_unchecked for signed integers)

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Mar 14, 2024
2 parents 6f3eb1c + 787cb68 commit 10a447d
Show file tree
Hide file tree
Showing 80 changed files with 2,500 additions and 740 deletions.
12 changes: 6 additions & 6 deletions compiler/rustc_const_eval/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -374,12 +374,6 @@ const_eval_unallowed_op_in_const_context =
const_eval_unavailable_target_features_for_fn =
calling a function that requires unavailable target features: {$unavailable_feats}
const_eval_undefined_behavior =
it is undefined behavior to use this value
const_eval_undefined_behavior_note =
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.
const_eval_uninhabited_enum_variant_read =
read discriminant of an uninhabited enum variant
const_eval_uninhabited_enum_variant_written =
Expand Down Expand Up @@ -434,6 +428,12 @@ const_eval_validation_expected_raw_ptr = expected a raw pointer
const_eval_validation_expected_ref = expected a reference
const_eval_validation_expected_str = expected a string
const_eval_validation_failure =
it is undefined behavior to use this value
const_eval_validation_failure_note =
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.
const_eval_validation_front_matter_invalid_value = constructing invalid value
const_eval_validation_front_matter_invalid_value_with_path = constructing invalid value at {$path}
Expand Down
14 changes: 6 additions & 8 deletions compiler/rustc_const_eval/src/const_eval/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ use std::mem;

use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, Diagnostic, IntoDiagArg};
use rustc_hir::CRATE_HIR_ID;
use rustc_middle::mir::interpret::Provenance;
use rustc_middle::mir::AssertKind;
use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::{layout::LayoutError, ConstInt};
use rustc_span::{Span, Symbol, DUMMY_SP};

use super::{CompileTimeInterpreter, InterpCx};
use super::CompileTimeInterpreter;
use crate::errors::{self, FrameNote, ReportErrorExt};
use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, MachineStopType};
use crate::interpret::{ErrorHandled, Frame, InterpError, InterpErrorInfo, MachineStopType};

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

pub fn get_span_and_frames<'tcx, 'mir>(
tcx: TyCtxtAt<'tcx>,
machine: &CompileTimeInterpreter<'mir, 'tcx>,
stack: &[Frame<'mir, 'tcx, impl Provenance, impl Sized>],
) -> (Span, Vec<errors::FrameNote>)
where
'tcx: 'mir,
{
let mut stacktrace =
InterpCx::<CompileTimeInterpreter<'mir, 'tcx>>::generate_stacktrace_from_stack(
&machine.stack,
);
let mut stacktrace = Frame::generate_stacktrace_from_stack(stack);
// Filter out `requires_caller_location` frames.
stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx));
let span = stacktrace.first().map(|f| f.span).unwrap_or(tcx.span);
Expand Down Expand Up @@ -170,7 +168,7 @@ pub(super) fn lint<'tcx, 'mir, L>(
) where
L: for<'a> rustc_errors::LintDiagnostic<'a, ()>,
{
let (span, frames) = get_span_and_frames(tcx, machine);
let (span, frames) = get_span_and_frames(tcx, &machine.stack);

tcx.emit_node_span_lint(
lint,
Expand Down
162 changes: 74 additions & 88 deletions compiler/rustc_const_eval/src/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ use crate::errors;
use crate::errors::ConstEvalError;
use crate::interpret::eval_nullary_intrinsic;
use crate::interpret::{
create_static_alloc, intern_const_alloc_recursive, take_static_root_alloc, CtfeValidationMode,
GlobalId, Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind,
OpTy, RefTracking, StackPopCleanup,
create_static_alloc, intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate,
InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking,
StackPopCleanup,
};

// Returns a pointer to where the result lives
#[instrument(level = "trace", skip(ecx, body), ret)]
fn eval_body_using_ecx<'mir, 'tcx>(
#[instrument(level = "trace", skip(ecx, body))]
fn eval_body_using_ecx<'mir, 'tcx, R: InterpretationResult<'tcx>>(
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
cid: GlobalId<'tcx>,
body: &'mir mir::Body<'tcx>,
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
) -> InterpResult<'tcx, R> {
trace!(?ecx.param_env);
let tcx = *ecx.tcx;
assert!(
Expand Down Expand Up @@ -84,7 +84,10 @@ fn eval_body_using_ecx<'mir, 'tcx>(
// Intern the result
intern_const_alloc_recursive(ecx, intern_kind, &ret)?;

Ok(ret)
// Since evaluation had no errors, validate the resulting constant.
const_validate_mplace(&ecx, &ret, cid)?;

Ok(R::make_result(ret, ecx))
}

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

let instance = ty::Instance::mono(tcx, def_id.to_def_id());
let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None };
let mut ecx = InterpCx::new(
tcx,
tcx.def_span(def_id),
ty::ParamEnv::reveal_all(),
// Statics (and promoteds inside statics) may access other statics, because unlike consts
// they do not have to behave "as if" they were evaluated at runtime.
CompileTimeInterpreter::new(CanAccessMutGlobal::Yes, CheckAlignment::Error),
);
let alloc_id = eval_in_interpreter(&mut ecx, cid, true)?.alloc_id;
let alloc = take_static_root_alloc(&mut ecx, alloc_id);
let alloc = tcx.mk_const_alloc(alloc);
Ok(alloc)
eval_in_interpreter(tcx, cid, ty::ParamEnv::reveal_all())
}

pub trait InterpretationResult<'tcx> {
/// This function takes the place where the result of the evaluation is stored
/// and prepares it for returning it in the appropriate format needed by the specific
/// evaluation query.
fn make_result<'mir>(
mplace: MPlaceTy<'tcx>,
ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
) -> Self;
}

impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> {
fn make_result<'mir>(
mplace: MPlaceTy<'tcx>,
_ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
) -> Self {
ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty }
}
}

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

let cid = key.value;
eval_in_interpreter(tcx, key.value, key.param_env)
}

fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
tcx: TyCtxt<'tcx>,
cid: GlobalId<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Result<R, ErrorHandled> {
let def = cid.instance.def.def_id();
let is_static = tcx.is_static(def);

let mut ecx = InterpCx::new(
tcx,
tcx.def_span(def),
key.param_env,
param_env,
// Statics (and promoteds inside statics) may access mutable global memory, because unlike consts
// they do not have to behave "as if" they were evaluated at runtime.
// For consts however we want to ensure they behave "as if" they were evaluated at runtime,
// so we have to reject reading mutable global memory.
CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
);
eval_in_interpreter(&mut ecx, cid, is_static)
}

pub fn eval_in_interpreter<'mir, 'tcx>(
ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
cid: GlobalId<'tcx>,
is_static: bool,
) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
// `is_static` just means "in static", it could still be a promoted!
debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some());

let res = ecx.load_mir(cid.instance.def, cid.promoted);
match res.and_then(|body| eval_body_using_ecx(ecx, cid, body)) {
Err(error) => {
let (error, backtrace) = error.into_parts();
backtrace.print_backtrace();

let (kind, instance) = if is_static {
("static", String::new())
} else {
// If the current item has generics, we'd like to enrich the message with the
// instance and its args: to show the actual compile-time values, in addition to
// the expression, leading to the const eval error.
let instance = &cid.instance;
if !instance.args.is_empty() {
let instance = with_no_trimmed_paths!(instance.to_string());
("const_with_path", instance)
} else {
("const", String::new())
}
};

Err(super::report(
*ecx.tcx,
error,
None,
|| super::get_span_and_frames(ecx.tcx, &ecx.machine),
|span, frames| ConstEvalError {
span,
error_kind: kind,
instance,
frame_notes: frames,
},
))
}
Ok(mplace) => {
// Since evaluation had no errors, validate the resulting constant.

// Temporarily allow access to the static_root_ids for the purpose of validation.
let static_root_ids = ecx.machine.static_root_ids.take();
let res = const_validate_mplace(&ecx, &mplace, cid);
ecx.machine.static_root_ids = static_root_ids;

let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();

// Validation failed, report an error.
if let Err(error) = res {
Err(const_report_error(&ecx, error, alloc_id))
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)).map_err(|error| {
let (error, backtrace) = error.into_parts();
backtrace.print_backtrace();

let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) {
("static", String::new())
} else {
// If the current item has generics, we'd like to enrich the message with the
// instance and its args: to show the actual compile-time values, in addition to
// the expression, leading to the const eval error.
let instance = &cid.instance;
if !instance.args.is_empty() {
let instance = with_no_trimmed_paths!(instance.to_string());
("const_with_path", instance)
} else {
// Convert to raw constant
Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty })
("const", String::new())
}
}
}
};

super::report(
*ecx.tcx,
error,
None,
|| super::get_span_and_frames(ecx.tcx, ecx.stack()),
|span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames },
)
})
}

#[inline(always)]
pub fn const_validate_mplace<'mir, 'tcx>(
fn const_validate_mplace<'mir, 'tcx>(
ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
mplace: &MPlaceTy<'tcx>,
cid: GlobalId<'tcx>,
) -> InterpResult<'tcx> {
) -> Result<(), ErrorHandled> {
let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
let mut ref_tracking = RefTracking::new(mplace.clone());
let mut inner = false;
while let Some((mplace, path)) = ref_tracking.todo.pop() {
Expand All @@ -418,15 +401,18 @@ pub fn const_validate_mplace<'mir, 'tcx>(
CtfeValidationMode::Const { allow_immutable_unsafe_cell: !inner }
}
};
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?;
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)
// Instead of just reporting the `InterpError` via the usual machinery, we give a more targetted
// error about the validation failure.
.map_err(|error| report_validation_error(&ecx, error, alloc_id))?;
inner = true;
}

Ok(())
}

#[inline(always)]
pub fn const_report_error<'mir, 'tcx>(
fn report_validation_error<'mir, 'tcx>(
ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
error: InterpErrorInfo<'tcx>,
alloc_id: AllocId,
Expand All @@ -444,7 +430,7 @@ pub fn const_report_error<'mir, 'tcx>(
*ecx.tcx,
error,
None,
|| crate::const_eval::get_span_and_frames(ecx.tcx, &ecx.machine),
move |span, frames| errors::UndefinedBehavior { span, ub_note, frames, raw_bytes },
|| crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()),
move |span, frames| errors::ValidationFailure { span, ub_note, frames, raw_bytes },
)
}
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/const_eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rustc_middle::mir::interpret::InterpErrorInfo;
use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::{self, Ty};

use crate::interpret::{format_interp_error, InterpCx};
use crate::interpret::format_interp_error;

mod error;
mod eval_queries;
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_const_eval/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,11 +412,11 @@ pub struct NullaryIntrinsicError {
}

#[derive(Diagnostic)]
#[diag(const_eval_undefined_behavior, code = E0080)]
pub struct UndefinedBehavior {
#[diag(const_eval_validation_failure, code = E0080)]
pub struct ValidationFailure {
#[primary_span]
pub span: Span,
#[note(const_eval_undefined_behavior_note)]
#[note(const_eval_validation_failure_note)]
pub ub_note: Option<()>,
#[subdiagnostic]
pub frames: Vec<FrameNote>,
Expand Down
Loading

0 comments on commit 10a447d

Please sign in to comment.