diff options
| author | Ralf Jung <post@ralfj.de> | 2023-11-26 16:57:13 +0100 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2023-12-07 17:46:36 +0100 |
| commit | 4d93590d59ca7d71ac401ce600fd0fe458a6375e (patch) | |
| tree | 4b74918362e10e3b473539398696b2a5ec0e8d06 /compiler/rustc_const_eval/src | |
| parent | cb863033423d895c0530ab749e4a3bca494b5c27 (diff) | |
| download | rust-4d93590d59ca7d71ac401ce600fd0fe458a6375e.tar.gz rust-4d93590d59ca7d71ac401ce600fd0fe458a6375e.zip | |
compile-time evaluation: emit a lint when a write through an immutable pointer occurs
Diffstat (limited to 'compiler/rustc_const_eval/src')
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/error.rs | 44 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/eval_queries.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/machine.rs | 65 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/errors.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/machine.rs | 15 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/memory.rs | 14 |
6 files changed, 112 insertions, 37 deletions
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index 4934fcc75ec..26cf3b3f2b0 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -1,14 +1,16 @@ use std::mem; use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, IntoDiagnostic, IntoDiagnosticArg}; +use rustc_hir::CRATE_HIR_ID; 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::{ErrorGuaranteed, Span, Symbol, DUMMY_SP}; -use super::InterpCx; +use super::{CompileTimeInterpreter, InterpCx}; use crate::errors::{self, FrameNote, ReportErrorExt}; -use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, Machine, MachineStopType}; +use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, MachineStopType}; /// The CTFE machine has some custom error kinds. #[derive(Clone, Debug)] @@ -57,16 +59,20 @@ impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind { } } -pub fn get_span_and_frames<'tcx, 'mir, M: Machine<'mir, 'tcx>>( - ecx: &InterpCx<'mir, 'tcx, M>, +pub fn get_span_and_frames<'tcx, 'mir>( + tcx: TyCtxtAt<'tcx>, + machine: &CompileTimeInterpreter<'mir, 'tcx>, ) -> (Span, Vec<errors::FrameNote>) where 'tcx: 'mir, { - let mut stacktrace = ecx.generate_stacktrace(); + let mut stacktrace = + InterpCx::<CompileTimeInterpreter<'mir, 'tcx>>::generate_stacktrace_from_stack( + &machine.stack, + ); // Filter out `requires_caller_location` frames. - stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*ecx.tcx)); - let span = stacktrace.first().map(|f| f.span).unwrap_or(ecx.tcx.span); + stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx)); + let span = stacktrace.first().map(|f| f.span).unwrap_or(tcx.span); let mut frames = Vec::new(); @@ -87,7 +93,7 @@ where let mut last_frame: Option<errors::FrameNote> = None; for frame_info in &stacktrace { - let frame = frame_info.as_note(*ecx.tcx); + let frame = frame_info.as_note(*tcx); match last_frame.as_mut() { Some(last_frame) if last_frame.span == frame.span @@ -156,3 +162,25 @@ where } } } + +/// Emit a lint from a const-eval situation. +// Even if this is unused, please don't remove it -- chances are we will need to emit a lint during const-eval again in the future! +pub(super) fn lint<'tcx, 'mir, L>( + tcx: TyCtxtAt<'tcx>, + machine: &CompileTimeInterpreter<'mir, 'tcx>, + lint: &'static rustc_session::lint::Lint, + decorator: impl FnOnce(Vec<errors::FrameNote>) -> L, +) where + L: for<'a> rustc_errors::DecorateLint<'a, ()>, +{ + let (span, frames) = get_span_and_frames(tcx, machine); + + tcx.emit_spanned_lint( + lint, + // We use the root frame for this so the crate that defines the const defines whether the + // lint is emitted. + machine.stack.first().and_then(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID), + span, + decorator(frames), + ); +} diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 9ca5ce40e76..9d22df50d4f 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -338,7 +338,7 @@ pub fn eval_in_interpreter<'mir, 'tcx>( *ecx.tcx, error, None, - || super::get_span_and_frames(&ecx), + || super::get_span_and_frames(ecx.tcx, &ecx.machine), |span, frames| ConstEvalError { span, error_kind: kind, @@ -419,7 +419,7 @@ pub fn const_report_error<'mir, 'tcx>( *ecx.tcx, error, None, - || crate::const_eval::get_span_and_frames(ecx), + || crate::const_eval::get_span_and_frames(ecx.tcx, &ecx.machine), move |span, frames| errors::UndefinedBehavior { span, ub_note, frames, raw_bytes }, ) } diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 015e19a0fff..761337126ef 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -1,30 +1,30 @@ -use rustc_hir::def::DefKind; -use rustc_hir::LangItem; -use rustc_middle::mir; -use rustc_middle::mir::interpret::PointerArithmetic; -use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout}; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::Span; use std::borrow::Borrow; +use std::fmt; use std::hash::Hash; use std::ops::ControlFlow; +use rustc_ast::Mutability; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::IndexEntry; -use std::fmt; - -use rustc_ast::Mutability; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; +use rustc_hir::LangItem; +use rustc_middle::mir; use rustc_middle::mir::AssertMessage; +use rustc_middle::query::TyCtxtAt; +use rustc_middle::ty; +use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout}; +use rustc_session::lint::builtin::WRITES_THROUGH_IMMUTABLE_POINTER; use rustc_span::symbol::{sym, Symbol}; +use rustc_span::Span; use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi as CallAbi; use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ - self, compile_time_machine, AllocId, ConstAllocation, FnArg, FnVal, Frame, ImmTy, InterpCx, - InterpResult, OpTy, PlaceTy, Pointer, Scalar, + self, compile_time_machine, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, FnVal, + Frame, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, PointerArithmetic, Scalar, }; use super::error::*; @@ -671,7 +671,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } fn before_access_global( - _tcx: TyCtxt<'tcx>, + _tcx: TyCtxtAt<'tcx>, machine: &Self, alloc_id: AllocId, alloc: ConstAllocation<'tcx>, @@ -708,6 +708,45 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, } } } + + fn retag_ptr_value( + ecx: &mut InterpCx<'mir, 'tcx, Self>, + _kind: mir::RetagKind, + val: &ImmTy<'tcx, CtfeProvenance>, + ) -> InterpResult<'tcx, ImmTy<'tcx, CtfeProvenance>> { + if let ty::Ref(_, ty, mutbl) = val.layout.ty.kind() + && *mutbl == Mutability::Not + && ty.is_freeze(*ecx.tcx, ecx.param_env) + { + // This is a frozen shared reference, mark it immutable. + let place = ecx.ref_to_mplace(val)?; + let new_place = place.map_provenance(|p| p.map(CtfeProvenance::as_immutable)); + Ok(ImmTy::from_immediate(new_place.to_ref(ecx), val.layout)) + } else { + Ok(val.clone()) + } + } + + fn before_memory_write( + tcx: TyCtxtAt<'tcx>, + machine: &mut Self, + _alloc_extra: &mut Self::AllocExtra, + (_alloc_id, immutable): (AllocId, bool), + range: AllocRange, + ) -> InterpResult<'tcx> { + if range.size == Size::ZERO { + // Nothing to check. + return Ok(()); + } + // Reject writes through immutable pointers. + if immutable { + super::lint(tcx, machine, WRITES_THROUGH_IMMUTABLE_POINTER, |frames| { + crate::errors::WriteThroughImmutablePointer { frames } + }); + } + // Everything else is fine. + Ok(()) + } } // Please do not add any code below the above `Machine` trait impl. I (oli-obk) plan more cleanups diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 8343dc2dd35..46fb64fd5b3 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -402,6 +402,13 @@ pub struct ConstEvalError { pub frame_notes: Vec<FrameNote>, } +#[derive(LintDiagnostic)] +#[diag(const_eval_write_through_immutable_pointer)] +pub struct WriteThroughImmutablePointer { + #[subdiagnostic] + pub frames: Vec<FrameNote>, +} + #[derive(Diagnostic)] #[diag(const_eval_nullary_intrinsic_fail)] pub struct NullaryIntrinsicError { diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index fe603e5c1fe..5e69965512b 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -9,8 +9,9 @@ use std::hash::Hash; use rustc_apfloat::{Float, FloatConvert}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_middle::mir; +use rustc_middle::query::TyCtxtAt; +use rustc_middle::ty; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::DefId; use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi as CallAbi; @@ -293,7 +294,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// `def_id` is `Some` if this is the "lazy" allocation of a static. #[inline] fn before_access_global( - _tcx: TyCtxt<'tcx>, + _tcx: TyCtxtAt<'tcx>, _machine: &Self, _alloc_id: AllocId, _allocation: ConstAllocation<'tcx>, @@ -388,7 +389,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// need to mutate. #[inline(always)] fn before_memory_read( - _tcx: TyCtxt<'tcx>, + _tcx: TyCtxtAt<'tcx>, _machine: &Self, _alloc_extra: &Self::AllocExtra, _prov: (AllocId, Self::ProvenanceExtra), @@ -400,7 +401,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Hook for performing extra checks on a memory write access. #[inline(always)] fn before_memory_write( - _tcx: TyCtxt<'tcx>, + _tcx: TyCtxtAt<'tcx>, _machine: &mut Self, _alloc_extra: &mut Self::AllocExtra, _prov: (AllocId, Self::ProvenanceExtra), @@ -412,7 +413,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Hook for performing extra operations on a memory deallocation. #[inline(always)] fn before_memory_deallocation( - _tcx: TyCtxt<'tcx>, + _tcx: TyCtxtAt<'tcx>, _machine: &mut Self, _alloc_extra: &mut Self::AllocExtra, _prov: (AllocId, Self::ProvenanceExtra), @@ -515,7 +516,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// (CTFE and ConstProp) use the same instance. Here, we share that code. pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { type Provenance = CtfeProvenance; - type ProvenanceExtra = (); // FIXME extract the "immutable" bool? + type ProvenanceExtra = bool; // the "immutable" flag type ExtraFnVal = !; @@ -597,6 +598,6 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> { // We know `offset` is relative to the allocation, so we can use `into_parts`. let (prov, offset) = ptr.into_parts(); - Some((prov.alloc_id(), offset, ())) + Some((prov.alloc_id(), offset, prov.immutable())) } } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 13530e9d9c4..3fde6ae9b8e 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -339,7 +339,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Let the machine take some extra action let size = alloc.size(); M::before_memory_deallocation( - *self.tcx, + self.tcx, &mut self.machine, &mut alloc.extra, (alloc_id, prov), @@ -561,7 +561,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (val, Some(def_id)) } }; - M::before_access_global(*self.tcx, &self.machine, id, alloc, def_id, is_write)?; + M::before_access_global(self.tcx, &self.machine, id, alloc, def_id, is_write)?; // We got tcx memory. Let the machine initialize its "extra" stuff. M::adjust_allocation( self, @@ -626,7 +626,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { )?; if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc { let range = alloc_range(offset, size); - M::before_memory_read(*self.tcx, &self.machine, &alloc.extra, (alloc_id, prov), range)?; + M::before_memory_read(self.tcx, &self.machine, &alloc.extra, (alloc_id, prov), range)?; Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id })) } else { // Even in this branch we have to be sure that we actually access the allocation, in @@ -687,13 +687,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { { let parts = self.get_ptr_access(ptr, size)?; if let Some((alloc_id, offset, prov)) = parts { - let tcx = *self.tcx; + let tcx = self.tcx; // FIXME: can we somehow avoid looking up the allocation twice here? // We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`. let (alloc, machine) = self.get_alloc_raw_mut(alloc_id)?; let range = alloc_range(offset, size); M::before_memory_write(tcx, machine, &mut alloc.extra, (alloc_id, prov), range)?; - Ok(Some(AllocRefMut { alloc, range, tcx, alloc_id })) + Ok(Some(AllocRefMut { alloc, range, tcx: *tcx, alloc_id })) } else { Ok(None) } @@ -1133,7 +1133,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let src_alloc = self.get_alloc_raw(src_alloc_id)?; let src_range = alloc_range(src_offset, size); M::before_memory_read( - *tcx, + tcx, &self.machine, &src_alloc.extra, (src_alloc_id, src_prov), @@ -1163,7 +1163,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (dest_alloc, extra) = self.get_alloc_raw_mut(dest_alloc_id)?; let dest_range = alloc_range(dest_offset, size * num_copies); M::before_memory_write( - *tcx, + tcx, extra, &mut dest_alloc.extra, (dest_alloc_id, dest_prov), |
