Skip to content

Commit a65949a

Browse files
committed
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.).
1 parent d6eb0f5 commit a65949a

32 files changed

+899
-783
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub struct ImplCandidate<'tcx> {
4040

4141
enum GetSafeTransmuteErrorAndReason {
4242
Silent,
43-
Error { err_msg: String, safe_transmute_explanation: String },
43+
Error { err_msg: String, safe_transmute_explanation: Option<String> },
4444
}
4545

4646
struct UnsatisfiedConst(pub bool);

compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs

+22-8
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
557557
GetSafeTransmuteErrorAndReason::Error {
558558
err_msg,
559559
safe_transmute_explanation,
560-
} => (err_msg, Some(safe_transmute_explanation)),
560+
} => (err_msg, safe_transmute_explanation),
561561
}
562562
} else {
563563
(err_msg, None)
@@ -3061,28 +3061,33 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
30613061
return GetSafeTransmuteErrorAndReason::Silent;
30623062
};
30633063

3064+
let dst = trait_ref.args.type_at(0);
3065+
let src = trait_ref.args.type_at(1);
3066+
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
3067+
30643068
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
30653069
obligation.cause,
30663070
src_and_dst,
30673071
assume,
30683072
) {
30693073
Answer::No(reason) => {
3070-
let dst = trait_ref.args.type_at(0);
3071-
let src = trait_ref.args.type_at(1);
3072-
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
30733074
let safe_transmute_explanation = match reason {
30743075
rustc_transmute::Reason::SrcIsNotYetSupported => {
3075-
format!("analyzing the transmutability of `{src}` is not yet supported.")
3076+
format!("analyzing the transmutability of `{src}` is not yet supported")
30763077
}
30773078

30783079
rustc_transmute::Reason::DstIsNotYetSupported => {
3079-
format!("analyzing the transmutability of `{dst}` is not yet supported.")
3080+
format!("analyzing the transmutability of `{dst}` is not yet supported")
30803081
}
30813082

30823083
rustc_transmute::Reason::DstIsBitIncompatible => {
30833084
format!("at least one value of `{src}` isn't a bit-valid value of `{dst}`")
30843085
}
30853086

3087+
rustc_transmute::Reason::DstUninhabited => {
3088+
format!("`{dst}` is uninhabited")
3089+
}
3090+
30863091
rustc_transmute::Reason::DstMayHaveSafetyInvariants => {
30873092
format!("`{dst}` may carry safety invariants")
30883093
}
@@ -3128,14 +3133,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
31283133
format!("`{dst}` has an unknown layout")
31293134
}
31303135
};
3131-
GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation }
3136+
GetSafeTransmuteErrorAndReason::Error {
3137+
err_msg,
3138+
safe_transmute_explanation: Some(safe_transmute_explanation),
3139+
}
31323140
}
31333141
// Should never get a Yes at this point! We already ran it before, and did not get a Yes.
31343142
Answer::Yes => span_bug!(
31353143
span,
31363144
"Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
31373145
),
3138-
other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"),
3146+
// Reached when a different obligation (namely `Freeze`) causes the
3147+
// transmutability analysis to fail. In this case, silence the
3148+
// transmutability error message in favor of that more specific
3149+
// error.
3150+
Answer::If(_) => {
3151+
GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation: None }
3152+
}
31393153
}
31403154
}
31413155

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+35-7
Original file line numberDiff line numberDiff line change
@@ -314,12 +314,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
314314
.flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond))
315315
.collect(),
316316
Condition::IfTransmutable { src, dst } => {
317-
let trait_def_id = obligation.predicate.def_id();
317+
let transmute_trait = obligation.predicate.def_id();
318318
let assume_const = predicate.trait_ref.args.const_at(2);
319-
let make_obl = |from_ty, to_ty| {
320-
let trait_ref1 = ty::TraitRef::new(
319+
let make_transmute_obl = |from_ty, to_ty| {
320+
let trait_ref = ty::TraitRef::new(
321321
tcx,
322-
trait_def_id,
322+
transmute_trait,
323323
[
324324
ty::GenericArg::from(to_ty),
325325
ty::GenericArg::from(from_ty),
@@ -331,17 +331,45 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
331331
obligation.cause.clone(),
332332
obligation.recursion_depth + 1,
333333
obligation.param_env,
334-
trait_ref1,
334+
trait_ref,
335335
)
336336
};
337337

338+
let make_freeze_obl = |ty| {
339+
let trait_ref = ty::TraitRef::new(
340+
tcx,
341+
tcx.lang_items().freeze_trait().unwrap(),
342+
[ty::GenericArg::from(ty)],
343+
);
344+
Obligation::with_depth(
345+
tcx,
346+
obligation.cause.clone(),
347+
obligation.recursion_depth + 1,
348+
obligation.param_env,
349+
trait_ref,
350+
)
351+
};
352+
353+
let mut obls = vec![];
354+
355+
// If the source is a shared reference, it must be `Freeze`;
356+
// otherwise, transmuting could lead to data races.
357+
if src.mutability == Mutability::Not {
358+
obls.extend([make_freeze_obl(src.ty), make_freeze_obl(dst.ty)])
359+
}
360+
338361
// If Dst is mutable, check bidirectionally.
339362
// For example, transmuting bool -> u8 is OK as long as you can't update that u8
340363
// to be > 1, because you could later transmute the u8 back to a bool and get UB.
341364
match dst.mutability {
342-
Mutability::Not => vec![make_obl(src.ty, dst.ty)],
343-
Mutability::Mut => vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)],
365+
Mutability::Not => obls.push(make_transmute_obl(src.ty, dst.ty)),
366+
Mutability::Mut => obls.extend([
367+
make_transmute_obl(src.ty, dst.ty),
368+
make_transmute_obl(dst.ty, src.ty),
369+
]),
344370
}
371+
372+
obls
345373
}
346374
}
347375
}

compiler/rustc_transmute/src/layout/dfa.rs

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ impl<R> Transitions<R>
3535
where
3636
R: Ref,
3737
{
38+
#[allow(dead_code)]
3839
fn insert(&mut self, transition: Transition<R>, state: State) {
3940
match transition {
4041
Transition::Byte(b) => {
@@ -82,6 +83,7 @@ impl<R> Dfa<R>
8283
where
8384
R: Ref,
8485
{
86+
#[allow(dead_code)]
8587
pub(crate) fn unit() -> Self {
8688
let transitions: Map<State, Transitions<R>> = Map::default();
8789
let start = State::new();

compiler/rustc_transmute/src/layout/nfa.rs

+1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ where
160160
Self { transitions, start, accepting }
161161
}
162162

163+
#[allow(dead_code)]
163164
pub(crate) fn edges_from(&self, start: State) -> Option<&Map<Transition<R>, Set<State>>> {
164165
self.transitions.get(&start)
165166
}

0 commit comments

Comments
 (0)