Skip to content

Commit 0e27c99

Browse files
authored
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`
2 parents ecfc338 + 3aa14e3 commit 0e27c99

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
@@ -558,7 +558,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
558558
GetSafeTransmuteErrorAndReason::Error {
559559
err_msg,
560560
safe_transmute_explanation,
561-
} => (err_msg, Some(safe_transmute_explanation)),
561+
} => (err_msg, safe_transmute_explanation),
562562
}
563563
} else {
564564
(err_msg, None)
@@ -3068,28 +3068,33 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
30683068
return GetSafeTransmuteErrorAndReason::Silent;
30693069
};
30703070

3071+
let dst = trait_ref.args.type_at(0);
3072+
let src = trait_ref.args.type_at(1);
3073+
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
3074+
30713075
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
30723076
obligation.cause,
30733077
src_and_dst,
30743078
assume,
30753079
) {
30763080
Answer::No(reason) => {
3077-
let dst = trait_ref.args.type_at(0);
3078-
let src = trait_ref.args.type_at(1);
3079-
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
30803081
let safe_transmute_explanation = match reason {
30813082
rustc_transmute::Reason::SrcIsNotYetSupported => {
3082-
format!("analyzing the transmutability of `{src}` is not yet supported.")
3083+
format!("analyzing the transmutability of `{src}` is not yet supported")
30833084
}
30843085

30853086
rustc_transmute::Reason::DstIsNotYetSupported => {
3086-
format!("analyzing the transmutability of `{dst}` is not yet supported.")
3087+
format!("analyzing the transmutability of `{dst}` is not yet supported")
30873088
}
30883089

30893090
rustc_transmute::Reason::DstIsBitIncompatible => {
30903091
format!("at least one value of `{src}` isn't a bit-valid value of `{dst}`")
30913092
}
30923093

3094+
rustc_transmute::Reason::DstUninhabited => {
3095+
format!("`{dst}` is uninhabited")
3096+
}
3097+
30933098
rustc_transmute::Reason::DstMayHaveSafetyInvariants => {
30943099
format!("`{dst}` may carry safety invariants")
30953100
}
@@ -3135,14 +3140,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
31353140
format!("`{dst}` has an unknown layout")
31363141
}
31373142
};
3138-
GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation }
3143+
GetSafeTransmuteErrorAndReason::Error {
3144+
err_msg,
3145+
safe_transmute_explanation: Some(safe_transmute_explanation),
3146+
}
31393147
}
31403148
// Should never get a Yes at this point! We already ran it before, and did not get a Yes.
31413149
Answer::Yes => span_bug!(
31423150
span,
31433151
"Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
31443152
),
3145-
other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"),
3153+
// Reached when a different obligation (namely `Freeze`) causes the
3154+
// transmutability analysis to fail. In this case, silence the
3155+
// transmutability error message in favor of that more specific
3156+
// error.
3157+
Answer::If(_) => {
3158+
GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation: None }
3159+
}
31463160
}
31473161
}
31483162

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)