Skip to content

Commit c7b86c3

Browse files
committed
Delay interning errors to after validation
1 parent a42873e commit c7b86c3

24 files changed

+362
-653
lines changed

compiler/rustc_const_eval/messages.ftl

-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ const_eval_dangling_int_pointer =
4949
const_eval_dangling_null_pointer =
5050
{$bad_pointer_message}: null pointer is a dangling pointer (it has no provenance)
5151
52-
const_eval_dangling_ptr_in_final = encountered dangling pointer in final value of {const_eval_intern_kind}
5352
const_eval_dead_local =
5453
accessing a dead local variable
5554
const_eval_dealloc_immutable =

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_middle::traits::Reveal;
88
use rustc_middle::ty::layout::LayoutOf;
99
use rustc_middle::ty::print::with_no_trimmed_paths;
1010
use rustc_middle::ty::{self, Ty, TyCtxt};
11+
use rustc_session::lint;
1112
use rustc_span::def_id::LocalDefId;
1213
use rustc_span::Span;
1314
use rustc_target::abi::{self, Abi};
@@ -82,11 +83,23 @@ fn eval_body_using_ecx<'mir, 'tcx, R: InterpretationResult<'tcx>>(
8283
while ecx.step()? {}
8384

8485
// Intern the result
85-
intern_const_alloc_recursive(ecx, intern_kind, &ret)?;
86+
let found_bad_mutable_pointer = intern_const_alloc_recursive(ecx, intern_kind, &ret);
8687

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

91+
// Only report this after validation, as validaiton produces much better diagnostics.
92+
// FIXME: ensure validation always reports this and stop making interning care about it.
93+
if found_bad_mutable_pointer {
94+
let err_diag = errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind };
95+
ecx.tcx.emit_node_span_lint(
96+
lint::builtin::CONST_EVAL_MUTABLE_PTR_IN_FINAL_VALUE,
97+
ecx.best_lint_scope(),
98+
err_diag.span,
99+
err_diag,
100+
)
101+
}
102+
90103
Ok(R::make_result(ret, ecx))
91104
}
92105

@@ -401,7 +414,7 @@ fn const_validate_mplace<'mir, 'tcx>(
401414
CtfeValidationMode::Const { allow_immutable_unsafe_cell: !inner }
402415
}
403416
};
404-
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)
417+
ecx.const_validate_operand(mplace, path, &mut ref_tracking, mode)
405418
// Instead of just reporting the `InterpError` via the usual machinery, we give a more targetted
406419
// error about the validation failure.
407420
.map_err(|error| report_validation_error(&ecx, error, alloc_id))?;

compiler/rustc_const_eval/src/const_eval/valtrees.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ pub fn valtree_to_const_value<'tcx>(
323323

324324
valtree_into_mplace(&mut ecx, &place, valtree);
325325
dump_place(&ecx, &place);
326-
intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap();
326+
assert!(!intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place));
327327

328328
op_to_const(&ecx, &place.into(), /* for diagnostics */ false)
329329
}
@@ -359,7 +359,7 @@ fn valtree_to_ref<'tcx>(
359359

360360
valtree_into_mplace(ecx, &pointee_place, valtree);
361361
dump_place(ecx, &pointee_place);
362-
intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap();
362+
assert!(!intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place));
363363

364364
pointee_place.to_ref(&ecx.tcx)
365365
}

compiler/rustc_const_eval/src/errors.rs

-8
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,6 @@ use rustc_target::abi::{Size, WrappingRange};
1717

1818
use crate::interpret::InternKind;
1919

20-
#[derive(Diagnostic)]
21-
#[diag(const_eval_dangling_ptr_in_final)]
22-
pub(crate) struct DanglingPtrInFinal {
23-
#[primary_span]
24-
pub span: Span,
25-
pub kind: InternKind,
26-
}
27-
2820
#[derive(LintDiagnostic)]
2921
#[diag(const_eval_mutable_ptr_in_final)]
3022
pub(crate) struct MutablePtrInFinal {

compiler/rustc_const_eval/src/interpret/intern.rs

+10-18
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,15 @@
1616
use hir::def::DefKind;
1717
use rustc_ast::Mutability;
1818
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
19-
use rustc_errors::ErrorGuaranteed;
2019
use rustc_hir as hir;
2120
use rustc_middle::mir::interpret::{ConstAllocation, CtfeProvenance, InterpResult};
2221
use rustc_middle::query::TyCtxtAt;
2322
use rustc_middle::ty::layout::TyAndLayout;
24-
use rustc_session::lint;
2523
use rustc_span::def_id::LocalDefId;
2624
use rustc_span::sym;
2725

2826
use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy};
2927
use crate::const_eval;
30-
use crate::errors::{DanglingPtrInFinal, MutablePtrInFinal};
3128

3229
pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine<
3330
'mir,
@@ -138,7 +135,7 @@ pub fn intern_const_alloc_recursive<
138135
ecx: &mut InterpCx<'mir, 'tcx, M>,
139136
intern_kind: InternKind,
140137
ret: &MPlaceTy<'tcx>,
141-
) -> Result<(), ErrorGuaranteed> {
138+
) -> bool {
142139
// We are interning recursively, and for mutability we are distinguishing the "root" allocation
143140
// that we are starting in, and all other allocations that we are encountering recursively.
144141
let (base_mutability, inner_mutability, is_static) = match intern_kind {
@@ -258,21 +255,16 @@ pub fn intern_const_alloc_recursive<
258255
// pointers before deciding which allocations can be made immutable; but for now we are
259256
// okay with losing some potential for immutability here. This can anyway only affect
260257
// `static mut`.
261-
todo.extend(intern_shallow(ecx, alloc_id, inner_mutability).map_err(|()| {
262-
ecx.tcx.dcx().emit_err(DanglingPtrInFinal { span: ecx.tcx.span, kind: intern_kind })
263-
})?);
264-
}
265-
if found_bad_mutable_pointer {
266-
let err_diag = MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind };
267-
ecx.tcx.emit_node_span_lint(
268-
lint::builtin::CONST_EVAL_MUTABLE_PTR_IN_FINAL_VALUE,
269-
ecx.best_lint_scope(),
270-
err_diag.span,
271-
err_diag,
272-
)
258+
match intern_shallow(ecx, alloc_id, inner_mutability) {
259+
Ok(nested) => todo.extend(nested),
260+
Err(()) => {
261+
ecx.tcx
262+
.dcx()
263+
.span_delayed_bug(ecx.tcx.span, "validation did not reject dangling pointer");
264+
}
265+
}
273266
}
274-
275-
Ok(())
267+
found_bad_mutable_pointer
276268
}
277269

278270
/// Intern `ret`. This function assumes that `ret` references no other allocation.

compiler/rustc_const_eval/src/interpret/validity.rs

+81-37
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::num::NonZero;
1010
use either::{Left, Right};
1111

1212
use hir::def::DefKind;
13+
use hir::def_id::DefId;
1314
use rustc_ast::Mutability;
1415
use rustc_data_structures::fx::FxHashSet;
1516
use rustc_hir as hir;
@@ -27,9 +28,9 @@ use rustc_target::abi::{
2728
use std::hash::Hash;
2829

2930
use super::{
30-
format_interp_error, machine::AllocMap, AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy,
31-
Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Pointer, Projectable,
32-
Scalar, ValueVisitor,
31+
format_interp_error, AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx,
32+
InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar,
33+
ValueVisitor,
3334
};
3435

3536
// for the validation errors
@@ -433,7 +434,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
433434
throw_validation_failure!(self.path, PtrToUninhabited { ptr_kind, ty })
434435
}
435436
// Recursive checking
436-
if let Some(ref_tracking) = self.ref_tracking.as_deref_mut() {
437+
if self.ref_tracking.is_some() {
437438
// Determine whether this pointer expects to be pointing to something mutable.
438439
let ptr_expected_mutbl = match ptr_kind {
439440
PointerKind::Box => Mutability::Mut,
@@ -457,6 +458,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
457458
// Special handling for pointers to statics (irrespective of their type).
458459
assert!(!self.ecx.tcx.is_thread_local_static(did));
459460
assert!(self.ecx.tcx.is_static(did));
461+
// Return alloc mutability. For "root" statics we look at the type to account for interior
462+
// mutability; for nested statics we have no type and directly use the annotated mutability.
463+
let DefKind::Static { mutability, nested } = self.ecx.tcx.def_kind(did)
464+
else {
465+
bug!()
466+
};
460467
// Mode-specific checks
461468
match self.ctfe_mode {
462469
Some(
@@ -471,7 +478,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
471478
// trigger cycle errors if we try to compute the value of the other static
472479
// and that static refers back to us (potentially through a promoted).
473480
// This could miss some UB, but that's fine.
474-
skip_recursive_check = true;
481+
// We still walk nested allocations, as they are fundamentally part of this validation run.
482+
skip_recursive_check = !nested;
475483
}
476484
Some(CtfeValidationMode::Const { .. }) => {
477485
// We can't recursively validate `extern static`, so we better reject them.
@@ -481,28 +489,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
481489
}
482490
None => {}
483491
}
484-
// Return alloc mutability. For "root" statics we look at the type to account for interior
485-
// mutability; for nested statics we have no type and directly use the annotated mutability.
486-
let DefKind::Static { mutability, nested } = self.ecx.tcx.def_kind(did)
487-
else {
488-
bug!()
489-
};
490-
match (mutability, nested) {
491-
(Mutability::Mut, _) => Mutability::Mut,
492-
(Mutability::Not, true) => Mutability::Not,
493-
(Mutability::Not, false)
494-
if !self
495-
.ecx
496-
.tcx
497-
.type_of(did)
498-
.no_bound_vars()
499-
.expect("statics should not have generic parameters")
500-
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all()) =>
501-
{
502-
Mutability::Mut
503-
}
504-
(Mutability::Not, false) => Mutability::Not,
505-
}
492+
self.static_mutability(mutability, nested, did)
506493
}
507494
GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
508495
GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => {
@@ -535,7 +522,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
535522
}
536523
}
537524
let path = &self.path;
538-
ref_tracking.track(place, || {
525+
self.ref_tracking.as_deref_mut().unwrap().track(place, || {
539526
// We need to clone the path anyway, make sure it gets created
540527
// with enough space for the additional `Deref`.
541528
let mut new_path = Vec::with_capacity(path.len() + 1);
@@ -547,6 +534,25 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
547534
Ok(())
548535
}
549536

537+
fn static_mutability(&self, mutability: Mutability, nested: bool, did: DefId) -> Mutability {
538+
match (mutability, nested) {
539+
(Mutability::Mut, _) => Mutability::Mut,
540+
(Mutability::Not, true) => Mutability::Not,
541+
(Mutability::Not, false)
542+
if !self
543+
.ecx
544+
.tcx
545+
.type_of(did)
546+
.no_bound_vars()
547+
.expect("statics should not have generic parameters")
548+
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all()) =>
549+
{
550+
Mutability::Mut
551+
}
552+
(Mutability::Not, false) => Mutability::Not,
553+
}
554+
}
555+
550556
/// Check if this is a value of primitive type, and if yes check the validity of the value
551557
/// at that type. Return `true` if the type is indeed primitive.
552558
///
@@ -599,6 +605,15 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
599605
if place.layout.is_unsized() {
600606
self.check_wide_ptr_meta(place.meta(), place.layout)?;
601607
}
608+
if self.ref_tracking.is_some()
609+
&& let Some(alloc_id) = place.ptr().provenance.and_then(|p| p.get_alloc_id())
610+
&& self.ecx.tcx.try_get_global_alloc(alloc_id).is_none()
611+
{
612+
throw_validation_failure!(
613+
self.path,
614+
DanglingPtrUseAfterFree { ptr_kind: PointerKind::Ref(Mutability::Not) }
615+
)
616+
}
602617
Ok(true)
603618
}
604619
ty::Ref(_, _ty, mutbl) => {
@@ -708,9 +723,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
708723
if let Some(mplace) = op.as_mplace_or_imm().left() {
709724
if let Some(alloc_id) = mplace.ptr().provenance.and_then(|p| p.get_alloc_id()) {
710725
let mutability = match self.ecx.tcx.global_alloc(alloc_id) {
711-
GlobalAlloc::Static(_) => {
712-
self.ecx.memory.alloc_map.get(alloc_id).unwrap().1.mutability
713-
}
726+
GlobalAlloc::Static(id) => match self.ecx.tcx.def_kind(id) {
727+
DefKind::Static { nested, mutability } => {
728+
self.static_mutability(mutability, nested, id)
729+
}
730+
_ => bug!(),
731+
},
714732
GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
715733
_ => span_bug!(self.ecx.tcx.span, "not a memory allocation"),
716734
};
@@ -975,15 +993,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
975993
path: Vec<PathElem>,
976994
ref_tracking: Option<&mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>>,
977995
ctfe_mode: Option<CtfeValidationMode>,
978-
) -> InterpResult<'tcx> {
996+
) -> InterpResult<'tcx, Vec<PathElem>> {
979997
trace!("validate_operand_internal: {:?}, {:?}", *op, op.layout.ty);
980998

981999
// Construct a visitor
9821000
let mut visitor = ValidityVisitor { path, ref_tracking, ctfe_mode, ecx: self };
9831001

9841002
// Run it.
9851003
match self.run_for_validation(|| visitor.visit_value(op)) {
986-
Ok(()) => Ok(()),
1004+
Ok(()) => Ok(visitor.path),
9871005
// Pass through validation failures and "invalid program" issues.
9881006
Err(err)
9891007
if matches!(
@@ -1003,7 +1021,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
10031021
}
10041022
}
10051023
}
1024+
}
10061025

1026+
impl<'mir, 'tcx> InterpCx<'mir, 'tcx, crate::const_eval::CompileTimeInterpreter<'mir, 'tcx>> {
10071027
/// This function checks the data at `op` to be const-valid.
10081028
/// `op` is assumed to cover valid memory if it is an indirect operand.
10091029
/// It will error if the bits at the destination do not match the ones described by the layout.
@@ -1017,14 +1037,38 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
10171037
#[inline(always)]
10181038
pub(crate) fn const_validate_operand(
10191039
&self,
1020-
op: &OpTy<'tcx, M::Provenance>,
1040+
mplace: MPlaceTy<'tcx>,
10211041
path: Vec<PathElem>,
1022-
ref_tracking: &mut RefTracking<MPlaceTy<'tcx, M::Provenance>, Vec<PathElem>>,
1042+
ref_tracking: &mut RefTracking<MPlaceTy<'tcx>, Vec<PathElem>>,
10231043
ctfe_mode: CtfeValidationMode,
10241044
) -> InterpResult<'tcx> {
1025-
self.validate_operand_internal(op, path, Some(ref_tracking), Some(ctfe_mode))
1045+
let prov = mplace.ptr().provenance;
1046+
let path = self.validate_operand_internal(
1047+
&mplace.into(),
1048+
path,
1049+
Some(ref_tracking),
1050+
Some(ctfe_mode),
1051+
)?;
1052+
1053+
// There was no error, so let's check the rest of the relocations in the pointed-to allocation for
1054+
// dangling pointers.
1055+
if let Some(prov) = prov
1056+
&& let Some(GlobalAlloc::Memory(alloc)) = self.tcx.try_get_global_alloc(prov.alloc_id())
1057+
{
1058+
for (_, prov) in alloc.0.provenance().ptrs().iter() {
1059+
if self.tcx.try_get_global_alloc(prov.alloc_id()).is_none() {
1060+
throw_validation_failure!(
1061+
path,
1062+
DanglingPtrUseAfterFree { ptr_kind: PointerKind::Ref(Mutability::Not) }
1063+
)
1064+
}
1065+
}
1066+
}
1067+
Ok(())
10261068
}
1069+
}
10271070

1071+
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
10281072
/// This function checks the data at `op` to be runtime-valid.
10291073
/// `op` is assumed to cover valid memory if it is an indirect operand.
10301074
/// It will error if the bits at the destination do not match the ones described by the layout.
@@ -1034,6 +1078,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
10341078
// still correct to not use `ctfe_mode`: that mode is for validation of the final constant
10351079
// value, it rules out things like `UnsafeCell` in awkward places. It also can make checking
10361080
// recurse through references which, for now, we don't want here, either.
1037-
self.validate_operand_internal(op, vec![], None, None)
1081+
self.validate_operand_internal(op, vec![], None, None).map(drop)
10381082
}
10391083
}

compiler/rustc_const_eval/src/util/caller_location.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@ pub(crate) fn const_caller_location_provider(
6464
);
6565

6666
let loc_place = alloc_caller_location(&mut ecx, file, line, col);
67-
if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
68-
bug!("intern_const_alloc_recursive should not error in this case")
69-
}
67+
assert!(!intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place));
7068
mir::ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr(), &tcx))
7169
}

tests/ui/consts/const-eval/heap/dealloc_intrinsic_dangling.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22
#![feature(const_heap)]
33
#![feature(const_mut_refs)]
44

5+
// Strip out raw byte dumps to make comparison platform-independent:
6+
//@ normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)"
7+
//@ normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?(<imm>)?─*╼ )+ *│.*" -> "HEX_DUMP"
8+
//@ normalize-stderr-test "HEX_DUMP\s*\n\s*HEX_DUMP" -> "HEX_DUMP"
9+
510
use std::intrinsics;
611

712
const _X: &'static u8 = unsafe {
8-
//~^ error: dangling pointer in final value of constant
13+
//~^ error: it is undefined behavior to use this value
914
let ptr = intrinsics::const_allocate(4, 4);
1015
intrinsics::const_deallocate(ptr, 4, 4);
1116
&*ptr

0 commit comments

Comments
 (0)