compile-time evaluation: emit a lint when a write through an immutable pointer occurs
This commit is contained in:
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user