rust/compiler/rustc_const_eval/src/util/alignment.rs
lcnr 9cba14b95b use TypingEnv when no infcx is available
the behavior of the type system not only depends on the current
assumptions, but also the currentnphase of the compiler. This is
mostly necessary as we need to decide whether and how to reveal
opaque types. We track this via the `TypingMode`.
2024-11-18 10:38:56 +01:00

74 lines
2.4 KiB
Rust

use rustc_abi::Align;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, TyCtxt};
use tracing::debug;
/// Returns `true` if this place is allowed to be less aligned
/// than its containing struct (because it is within a packed
/// struct).
pub fn is_disaligned<'tcx, L>(
tcx: TyCtxt<'tcx>,
local_decls: &L,
typing_env: ty::TypingEnv<'tcx>,
place: Place<'tcx>,
) -> bool
where
L: HasLocalDecls<'tcx>,
{
debug!("is_disaligned({:?})", place);
let Some(pack) = is_within_packed(tcx, local_decls, place) else {
debug!("is_disaligned({:?}) - not within packed", place);
return false;
};
let ty = place.ty(local_decls, tcx).ty;
let unsized_tail = || tcx.struct_tail_for_codegen(ty, typing_env);
match tcx.layout_of(typing_env.as_query_input(ty)) {
Ok(layout)
if layout.align.abi <= pack
&& (layout.is_sized()
|| matches!(unsized_tail().kind(), ty::Slice(..) | ty::Str)) =>
{
// If the packed alignment is greater or equal to the field alignment, the type won't be
// further disaligned.
// However we need to ensure the field is sized; for unsized fields, `layout.align` is
// just an approximation -- except when the unsized tail is a slice, where the alignment
// is fully determined by the type.
debug!(
"is_disaligned({:?}) - align = {}, packed = {}; not disaligned",
place,
layout.align.abi.bytes(),
pack.bytes()
);
false
}
_ => {
// We cannot figure out the layout. Conservatively assume that this is disaligned.
debug!("is_disaligned({:?}) - true", place);
true
}
}
}
pub fn is_within_packed<'tcx, L>(
tcx: TyCtxt<'tcx>,
local_decls: &L,
place: Place<'tcx>,
) -> Option<Align>
where
L: HasLocalDecls<'tcx>,
{
place
.iter_projections()
.rev()
// Stop at `Deref`; standard ABI alignment applies there.
.take_while(|(_base, elem)| !matches!(elem, ProjectionElem::Deref))
// Consider the packed alignments at play here...
.filter_map(|(base, _elem)| {
base.ty(local_decls, tcx).ty.ty_adt_def().and_then(|adt| adt.repr().pack)
})
// ... and compute their minimum.
// The overall smallest alignment is what matters.
.min()
}