Make alignment checks a future incompat lint
This commit is contained in:
@@ -14,10 +14,15 @@ use std::ptr;
|
||||
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir::CRATE_HIR_ID;
|
||||
use rustc_middle::mir::display_allocation;
|
||||
use rustc_middle::mir::interpret::UndefinedBehaviorInfo;
|
||||
use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
|
||||
use rustc_session::lint::builtin::INVALID_ALIGNMENT;
|
||||
use rustc_target::abi::{Align, HasDataLayout, Size};
|
||||
|
||||
use crate::const_eval::CheckAlignment;
|
||||
|
||||
use super::{
|
||||
alloc_range, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg, GlobalAlloc, InterpCx,
|
||||
InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Provenance, Scalar,
|
||||
@@ -377,7 +382,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
ptr,
|
||||
size,
|
||||
align,
|
||||
/* force_alignment_check */ true,
|
||||
CheckAlignment::Error,
|
||||
msg,
|
||||
|alloc_id, _, _| {
|
||||
let (size, align) = self.get_live_alloc_size_and_align(alloc_id)?;
|
||||
@@ -396,7 +401,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
align: Align,
|
||||
force_alignment_check: bool,
|
||||
check: CheckAlignment,
|
||||
msg: CheckInAllocMsg,
|
||||
alloc_size: impl FnOnce(
|
||||
AllocId,
|
||||
@@ -404,19 +409,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
M::ProvenanceExtra,
|
||||
) -> InterpResult<'tcx, (Size, Align, T)>,
|
||||
) -> InterpResult<'tcx, Option<T>> {
|
||||
fn check_offset_align<'tcx>(offset: u64, align: Align) -> InterpResult<'tcx> {
|
||||
if offset % align.bytes() == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
// The biggest power of two through which `offset` is divisible.
|
||||
let offset_pow2 = 1 << offset.trailing_zeros();
|
||||
throw_ub!(AlignmentCheckFailed {
|
||||
has: Align::from_bytes(offset_pow2).unwrap(),
|
||||
required: align,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Ok(match self.ptr_try_get_alloc_id(ptr) {
|
||||
Err(addr) => {
|
||||
// We couldn't get a proper allocation. This is only okay if the access size is 0,
|
||||
@@ -425,8 +417,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
throw_ub!(DanglingIntPointer(addr, msg));
|
||||
}
|
||||
// Must be aligned.
|
||||
if force_alignment_check {
|
||||
check_offset_align(addr, align)?;
|
||||
if check.should_check() {
|
||||
self.check_offset_align(addr, align, check)?;
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -449,16 +441,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
}
|
||||
// Test align. Check this last; if both bounds and alignment are violated
|
||||
// we want the error to be about the bounds.
|
||||
if force_alignment_check {
|
||||
if check.should_check() {
|
||||
if M::use_addr_for_alignment_check(self) {
|
||||
// `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
|
||||
check_offset_align(ptr.addr().bytes(), align)?;
|
||||
self.check_offset_align(ptr.addr().bytes(), align, check)?;
|
||||
} else {
|
||||
// Check allocation alignment and offset alignment.
|
||||
if alloc_align.bytes() < align.bytes() {
|
||||
throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align });
|
||||
self.alignment_check_failed(alloc_align, align, check)?;
|
||||
}
|
||||
check_offset_align(offset.bytes(), align)?;
|
||||
self.check_offset_align(offset.bytes(), align, check)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,6 +460,55 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn check_offset_align(
|
||||
&self,
|
||||
offset: u64,
|
||||
align: Align,
|
||||
check: CheckAlignment,
|
||||
) -> InterpResult<'tcx> {
|
||||
if offset % align.bytes() == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
// The biggest power of two through which `offset` is divisible.
|
||||
let offset_pow2 = 1 << offset.trailing_zeros();
|
||||
self.alignment_check_failed(Align::from_bytes(offset_pow2).unwrap(), align, check)
|
||||
}
|
||||
}
|
||||
|
||||
fn alignment_check_failed(
|
||||
&self,
|
||||
has: Align,
|
||||
required: Align,
|
||||
check: CheckAlignment,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
match check {
|
||||
CheckAlignment::Error => {
|
||||
throw_ub!(AlignmentCheckFailed { has, required })
|
||||
}
|
||||
CheckAlignment::No => span_bug!(
|
||||
self.cur_span(),
|
||||
"`alignment_check_failed` called when no alignment check requested"
|
||||
),
|
||||
CheckAlignment::FutureIncompat => self.tcx.struct_span_lint_hir(
|
||||
INVALID_ALIGNMENT,
|
||||
self.stack().iter().find_map(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID),
|
||||
self.cur_span(),
|
||||
UndefinedBehaviorInfo::AlignmentCheckFailed { has, required }.to_string(),
|
||||
|db| {
|
||||
let mut stacktrace = self.generate_stacktrace();
|
||||
// Filter out `requires_caller_location` frames.
|
||||
stacktrace
|
||||
.retain(|frame| !frame.instance.def.requires_caller_location(*self.tcx));
|
||||
for frame in stacktrace {
|
||||
db.span_label(frame.span, format!("inside `{}`", frame.instance));
|
||||
}
|
||||
db
|
||||
},
|
||||
),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocation accessors
|
||||
|
||||
Reference in New Issue
Block a user