Skip to content

Commit

Permalink
don't ICE when encountering an extern type field during validation
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Jun 22, 2024
1 parent d03d6c0 commit b80b09e
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 40 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_const_eval/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ const_eval_exact_div_has_remainder =
const_eval_extern_static =
cannot access extern static ({$did})
const_eval_extern_type_field = `extern type` field does not have a known offset
const_eval_fn_ptr_call =
function pointers need an RFC before allowed to be called in {const_eval_const_context}s
const_eval_for_loop_into_iter_non_const =
Expand Down
81 changes: 49 additions & 32 deletions compiler/rustc_const_eval/src/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,33 +386,8 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
CompileTimeMachine::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
);
let res = ecx.load_mir(cid.instance.def, cid.promoted);
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 {
("const", String::new())
}
};

super::report(
*ecx.tcx,
error,
DUMMY_SP,
|| super::get_span_and_frames(ecx.tcx, ecx.stack()),
|span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames },
)
})
res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body))
.map_err(|error| report_eval_error(&ecx, cid, error))
}

#[inline(always)]
Expand All @@ -438,24 +413,61 @@ fn const_validate_mplace<'tcx>(
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 targeted
// error about the validation failure.
.map_err(|error| report_validation_error(&ecx, error, alloc_id))?;
.map_err(|error| report_validation_error(&ecx, cid, error, alloc_id))?;
inner = true;
}

Ok(())
}

#[inline(always)]
#[inline(never)]
fn report_eval_error<'tcx>(
ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
cid: GlobalId<'tcx>,
error: InterpErrorInfo<'tcx>,
) -> ErrorHandled {
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 {
("const", String::new())
}
};

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

#[inline(never)]
fn report_validation_error<'tcx>(
ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
cid: GlobalId<'tcx>,
error: InterpErrorInfo<'tcx>,
alloc_id: AllocId,
) -> ErrorHandled {
if !matches!(error.kind(), InterpError::UndefinedBehavior(_)) {
// Some other error happened during validation, e.g. an unsupported operation.
return report_eval_error(ecx, cid, error);
}

let (error, backtrace) = error.into_parts();
backtrace.print_backtrace();

let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {});

let bytes = ecx.print_alloc_bytes_for_diagnostics(alloc_id);
let (size, align, _) = ecx.get_alloc_info(alloc_id);
let raw_bytes = errors::RawBytesNote { size: size.bytes(), align: align.bytes(), bytes };
Expand All @@ -465,6 +477,11 @@ fn report_validation_error<'tcx>(
error,
DUMMY_SP,
|| crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()),
move |span, frames| errors::ValidationFailure { span, ub_note, frames, raw_bytes },
move |span, frames| errors::ValidationFailure {
span,
ub_note: Some(()),
frames,
raw_bytes,
},
)
}
6 changes: 5 additions & 1 deletion compiler/rustc_const_eval/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ impl ReportErrorExt for UnsupportedOpInfo {
use crate::fluent_generated::*;
match self {
UnsupportedOpInfo::Unsupported(s) => s.clone().into(),
UnsupportedOpInfo::ExternTypeField => const_eval_extern_type_field,
UnsupportedOpInfo::UnsizedLocal => const_eval_unsized_local,
UnsupportedOpInfo::OverwritePartialPointer(_) => const_eval_partial_pointer_overwrite,
UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_copy,
Expand All @@ -845,7 +846,10 @@ impl ReportErrorExt for UnsupportedOpInfo {
// `ReadPointerAsInt(Some(info))` is never printed anyway, it only serves as an error to
// be further processed by validity checking which then turns it into something nice to
// print. So it's not worth the effort of having diagnostics that can print the `info`.
UnsizedLocal | Unsupported(_) | ReadPointerAsInt(_) => {}
UnsizedLocal
| UnsupportedOpInfo::ExternTypeField
| Unsupported(_)
| ReadPointerAsInt(_) => {}
OverwritePartialPointer(ptr) | ReadPartialPointer(ptr) => {
diag.arg("ptr", ptr);
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_const_eval/src/interpret/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use rustc_target::abi::{self, VariantIdx};
use tracing::{debug, instrument};

use super::{
throw_ub, throw_unsup_format, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
throw_ub, throw_unsup, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
Provenance, Scalar,
};

Expand Down Expand Up @@ -186,8 +186,8 @@ where
(base_meta, offset)
}
None => {
// We don't know the alignment of this field, so we cannot adjust.
throw_unsup_format!("`extern type` does not have a known offset")
// We cannot know the alignment of this field, so we cannot adjust.
throw_unsup!(ExternTypeField)
}
}
} else {
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_const_eval/src/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//! to be const-safe.

use std::fmt::Write;
use std::hash::Hash;
use std::num::NonZero;

use either::{Left, Right};
Expand All @@ -17,7 +18,8 @@ use rustc_hir as hir;
use rustc_middle::bug;
use rustc_middle::mir::interpret::{
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*,
UnsupportedOpInfo, ValidationErrorInfo,
ValidationErrorKind::{self, *},
};
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, Ty};
Expand All @@ -26,8 +28,6 @@ use rustc_target::abi::{
Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
};

use std::hash::Hash;

use super::{
err_ub, format_interp_error, machine::AllocMap, throw_ub, AllocId, AllocKind, CheckInAllocMsg,
GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
Expand Down Expand Up @@ -1028,7 +1028,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Err(err)
if matches!(
err.kind(),
err_ub!(ValidationError { .. }) | InterpError::InvalidProgram(_)
err_ub!(ValidationError { .. })
| InterpError::InvalidProgram(_)
| InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField)
) =>
{
Err(err)
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/mir/interpret/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,8 @@ pub enum UnsupportedOpInfo {
Unsupported(String),
/// Unsized local variables.
UnsizedLocal,
/// Extern type field with an indeterminate offset.
ExternTypeField,
//
// The variants below are only reachable from CTFE/const prop, miri will never emit them.
//
Expand Down
15 changes: 15 additions & 0 deletions tests/ui/consts/const-eval/validation-ice-extern-type-field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#![feature(extern_types)]

extern {
type Opaque;
}

struct ThinDst {
x: u8,
tail: Opaque,
}

const C1: &ThinDst = unsafe { std::mem::transmute(b"d".as_ptr()) };
//~ERROR: evaluation of constant value failed

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0080]: evaluation of constant value failed
--> $DIR/validation-ice-extern-type-field.rs:12:1
|
LL | const C1: &ThinDst = unsafe { std::mem::transmute(b"d".as_ptr()) };
| ^^^^^^^^^^^^^^^^^^ `extern type` field does not have a known offset

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0080`.

0 comments on commit b80b09e

Please sign in to comment.