1
Fork 0

Rollup merge of #123367 - jswrenn:layoutify, r=compiler-errors

Safe Transmute: Compute transmutability from `rustc_target::abi::Layout`

In its first step of computing transmutability, `rustc_transmutability` constructs a byte-level representation of type layout (`Tree`). Previously, this representation was computed for ADTs by inspecting the ADT definition and performing our own layout computations. This process was error-prone, verbose, and limited our ability to analyze many types (particularly default-repr types).

In this PR, we instead construct `Tree`s from `rustc_target::abi::Layout`s. This helps ensure that layout optimizations are reflected our analyses, and increases the kinds of types we can now analyze, including:
- default repr ADTs
- transparent unions
- `UnsafeCell`-containing types

Overall, this PR expands the expressvity of `rustc_transmutability` to be much closer to the transmutability analysis performed by miri. Future PRs will work to close the remaining gaps (e.g., support for `Box`, raw pointers, `NonZero*`, coroutines, etc.).

r? `@compiler-errors`
This commit is contained in:
Matthias Krüger 2024-04-08 22:06:21 +02:00 committed by GitHub
commit 0e27c99332
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 905 additions and 789 deletions

View file

@ -314,12 +314,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond))
.collect(),
Condition::IfTransmutable { src, dst } => {
let trait_def_id = obligation.predicate.def_id();
let transmute_trait = obligation.predicate.def_id();
let assume_const = predicate.trait_ref.args.const_at(2);
let make_obl = |from_ty, to_ty| {
let trait_ref1 = ty::TraitRef::new(
let make_transmute_obl = |from_ty, to_ty| {
let trait_ref = ty::TraitRef::new(
tcx,
trait_def_id,
transmute_trait,
[
ty::GenericArg::from(to_ty),
ty::GenericArg::from(from_ty),
@ -331,17 +331,45 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
trait_ref1,
trait_ref,
)
};
let make_freeze_obl = |ty| {
let trait_ref = ty::TraitRef::new(
tcx,
tcx.lang_items().freeze_trait().unwrap(),
[ty::GenericArg::from(ty)],
);
Obligation::with_depth(
tcx,
obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
trait_ref,
)
};
let mut obls = vec![];
// If the source is a shared reference, it must be `Freeze`;
// otherwise, transmuting could lead to data races.
if src.mutability == Mutability::Not {
obls.extend([make_freeze_obl(src.ty), make_freeze_obl(dst.ty)])
}
// If Dst is mutable, check bidirectionally.
// For example, transmuting bool -> u8 is OK as long as you can't update that u8
// to be > 1, because you could later transmute the u8 back to a bool and get UB.
match dst.mutability {
Mutability::Not => vec![make_obl(src.ty, dst.ty)],
Mutability::Mut => vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)],
Mutability::Not => obls.push(make_transmute_obl(src.ty, dst.ty)),
Mutability::Mut => obls.extend([
make_transmute_obl(src.ty, dst.ty),
make_transmute_obl(dst.ty, src.ty),
]),
}
obls
}
}
}