Skip to content

Commit eaee913

Browse files
committed
TransmuteFrom: normalize types, unify confirmation and error reporting
Refactor to share code between `TransmuteFrom`'s trait selection and error reporting code paths. Additionally normalizes the source and destination types, and gracefully handles normalization errors. Fixes #130413
1 parent 8dd5cd0 commit eaee913

File tree

13 files changed

+161
-83
lines changed

13 files changed

+161
-83
lines changed

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

Lines changed: 21 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
245245
span, "silent safe transmute error"
246246
);
247247
}
248+
GetSafeTransmuteErrorAndReason::Default => {
249+
(err_msg, None)
250+
}
248251
GetSafeTransmuteErrorAndReason::Error {
249252
err_msg,
250253
safe_transmute_explanation,
@@ -2219,38 +2222,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
22192222
trait_ref: ty::PolyTraitRef<'tcx>,
22202223
span: Span,
22212224
) -> GetSafeTransmuteErrorAndReason {
2222-
use rustc_transmute::Answer;
2223-
2224-
// Erase regions because layout code doesn't particularly care about regions.
2225-
let trait_ref =
2226-
self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref));
2227-
2228-
let src_and_dst = rustc_transmute::Types {
2229-
dst: trait_ref.args.type_at(0),
2230-
src: trait_ref.args.type_at(1),
2231-
};
2232-
let Some(assume) = rustc_transmute::Assume::from_const(
2233-
self.infcx.tcx,
2234-
obligation.param_env,
2235-
trait_ref.args.const_at(2),
2236-
) else {
2237-
self.dcx().span_delayed_bug(
2238-
span,
2239-
"Unable to construct rustc_transmute::Assume where it was previously possible",
2240-
);
2241-
return GetSafeTransmuteErrorAndReason::Silent;
2242-
};
2243-
2244-
let dst = trait_ref.args.type_at(0);
2245-
let src = trait_ref.args.type_at(1);
2246-
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
2247-
2248-
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
2249-
obligation.cause,
2250-
src_and_dst,
2251-
assume,
2225+
match crate::transmute::confirm_transmutability_candidate(
2226+
&self.infcx,
2227+
&obligation,
2228+
trait_ref,
22522229
) {
2253-
Answer::No(reason) => {
2230+
Ok(None) => {
2231+
self.dcx().span_delayed_bug(
2232+
span,
2233+
"unable to analyze transmutability where it was previously possible",
2234+
);
2235+
GetSafeTransmuteErrorAndReason::Silent
2236+
}
2237+
Ok(Some((src, dst, _, _))) => {
2238+
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
2239+
GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation: None }
2240+
}
2241+
Err(None) => GetSafeTransmuteErrorAndReason::Default,
2242+
Err(Some((src, dst, reason))) => {
2243+
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
22542244
let safe_transmute_explanation = match reason {
22552245
rustc_transmute::Reason::SrcIsNotYetSupported => {
22562246
format!("analyzing the transmutability of `{src}` is not yet supported")
@@ -2318,18 +2308,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
23182308
safe_transmute_explanation: Some(safe_transmute_explanation),
23192309
}
23202310
}
2321-
// Should never get a Yes at this point! We already ran it before, and did not get a Yes.
2322-
Answer::Yes => span_bug!(
2323-
span,
2324-
"Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
2325-
),
2326-
// Reached when a different obligation (namely `Freeze`) causes the
2327-
// transmutability analysis to fail. In this case, silence the
2328-
// transmutability error message in favor of that more specific
2329-
// error.
2330-
Answer::If(_) => {
2331-
GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation: None }
2332-
}
23332311
}
23342312
}
23352313

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub struct ImplCandidate<'tcx> {
4343

4444
enum GetSafeTransmuteErrorAndReason {
4545
Silent,
46+
Default,
4647
Error { err_msg: String, safe_transmute_explanation: Option<String> },
4748
}
4849

compiler/rustc_trait_selection/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,6 @@ pub mod infer;
4141
pub mod regions;
4242
pub mod solve;
4343
pub mod traits;
44+
mod transmute;
4445

4546
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }

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

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
292292
&mut self,
293293
obligation: &PolyTraitObligation<'tcx>,
294294
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
295-
use rustc_transmute::{Answer, Assume, Condition};
295+
use rustc_transmute::{Assume, Condition};
296296

297297
/// Generate sub-obligations for reference-to-reference transmutations.
298298
fn reference_obligations<'tcx>(
@@ -400,35 +400,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
400400
}
401401
}
402402

403-
let predicate = obligation.predicate.skip_binder();
403+
let trait_ref = obligation.predicate.to_poly_trait_ref();
404404

405-
let Some(assume) = rustc_transmute::Assume::from_const(
406-
self.infcx.tcx,
407-
obligation.param_env,
408-
predicate.trait_ref.args.const_at(2),
409-
) else {
410-
return Err(Unimplemented);
411-
};
412-
413-
let dst = predicate.trait_ref.args.type_at(0);
414-
let src = predicate.trait_ref.args.type_at(1);
415-
416-
debug!(?src, ?dst);
417-
let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);
418-
let maybe_transmutable = transmute_env.is_transmutable(
419-
obligation.cause.clone(),
420-
rustc_transmute::Types { dst, src },
421-
assume,
422-
);
423-
424-
let fully_flattened = match maybe_transmutable {
425-
Answer::No(_) => Err(Unimplemented)?,
426-
Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, cond, assume),
427-
Answer::Yes => vec![],
428-
};
429-
430-
debug!(?fully_flattened);
431-
Ok(fully_flattened)
405+
match crate::transmute::confirm_transmutability_candidate(
406+
&self.infcx,
407+
obligation,
408+
trait_ref,
409+
) {
410+
Ok(None) => Ok(vec![]),
411+
Ok(Some((_, _, assume, cond))) => {
412+
Ok(flatten_answer_tree(self.tcx(), obligation, cond, assume))
413+
}
414+
Err(_) => Err(SelectionError::Unimplemented),
415+
}
432416
}
433417

434418
/// This handles the case where an `auto trait Foo` impl is being used.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use rustc_infer::infer::InferCtxt;
2+
use rustc_infer::traits::{Obligation, ObligationCause};
3+
use rustc_middle::ty::{Binder, ParamEnv, PolyTraitRef, Ty, TyCtxt};
4+
use rustc_transmute::layout::rustc::Ref;
5+
use rustc_transmute::{Answer, Assume, Condition, Reason};
6+
7+
/// Attempts to confirm the given transmutability candidate.
8+
///
9+
/// The return type of this routine reflects that it is designed to support the
10+
/// needs of both trait selection and error reporting; it returns:
11+
/// - `Ok(None)` if the types are transmutable
12+
/// - `Ok(Some(src, dst, assumptions, condition))` if the `src` is transmutable
13+
/// into `dst` when `condition` holds under `assumptions`
14+
/// - `Err(None)` if transmutability cannot be assessed; e.g., due to a
15+
/// malformed `Assume`
16+
/// - `Err(Some((src, dst, reason))` if `src` is not transmutable into `dst`
17+
/// because of `reason`.
18+
pub(crate) fn confirm_transmutability_candidate<'tcx, P>(
19+
infcx: &InferCtxt<'tcx>,
20+
obligation: &Obligation<'tcx, P>,
21+
trait_ref: PolyTraitRef<'tcx>,
22+
) -> Result<
23+
Option<(Ty<'tcx>, Ty<'tcx>, Assume, Condition<Ref<'tcx>>)>,
24+
Option<(Ty<'tcx>, Ty<'tcx>, Reason<Ref<'tcx>>)>,
25+
> {
26+
/// Attempts to deeply normalize `ty`.
27+
fn try_normalize<'tcx>(
28+
tcx: TyCtxt<'tcx>,
29+
param_env: ParamEnv<'tcx>,
30+
ty: Binder<'tcx, Ty<'tcx>>,
31+
) -> Option<Binder<'tcx, Ty<'tcx>>> {
32+
use rustc_infer::infer::TyCtxtInferExt;
33+
34+
use crate::traits::ObligationCtxt;
35+
let infcx = tcx.infer_ctxt().with_next_trait_solver(true).build();
36+
let ocx = ObligationCtxt::new(&infcx);
37+
let cause = ObligationCause::dummy();
38+
let Ok(ty) = ocx.deeply_normalize(&cause, param_env, ty) else { return None };
39+
let errors = ocx.select_all_or_error();
40+
if errors.is_empty() { Some(ty) } else { None }
41+
}
42+
43+
let dst = trait_ref.map_bound(|tr| tr.args.type_at(0));
44+
let src = trait_ref.map_bound(|tr| tr.args.type_at(1));
45+
46+
let Some(dst) = try_normalize(infcx.tcx, obligation.param_env, dst) else {
47+
return Err(None);
48+
};
49+
50+
let Some(src) = try_normalize(infcx.tcx, obligation.param_env, src) else {
51+
return Err(None);
52+
};
53+
54+
// The immediate layouts of `src` and `dst` do not depend on lifetimes.
55+
let dst = dst.skip_binder();
56+
let src = src.skip_binder();
57+
58+
let Some(assume) = rustc_transmute::Assume::from_const(
59+
infcx.tcx,
60+
obligation.param_env,
61+
trait_ref.skip_binder().args.const_at(2),
62+
) else {
63+
return Err(None);
64+
};
65+
66+
let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(infcx);
67+
let maybe_transmutable = transmute_env.is_transmutable(
68+
obligation.cause.clone(),
69+
rustc_transmute::Types { dst, src },
70+
assume,
71+
);
72+
73+
match maybe_transmutable {
74+
Answer::No(reason) => return Err(Some((src, dst, reason))),
75+
Answer::If(cond) => Ok(Some((src, dst, assume, cond))),
76+
Answer::Yes => Ok(None),
77+
}
78+
}

compiler/rustc_transmute/src/layout/tree.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,11 @@ pub(crate) mod rustc {
195195
impl<'tcx> From<&LayoutError<'tcx>> for Err {
196196
fn from(err: &LayoutError<'tcx>) -> Self {
197197
match err {
198-
LayoutError::Unknown(..) | LayoutError::ReferencesError(..) => Self::UnknownLayout,
198+
LayoutError::Unknown(..)
199+
| LayoutError::ReferencesError(..)
200+
| LayoutError::NormalizationFailure(..) => Self::UnknownLayout,
199201
LayoutError::SizeOverflow(..) => Self::SizeOverflow,
200202
LayoutError::Cycle(err) => Self::TypeError(*err),
201-
err => unimplemented!("{:?}", err),
202203
}
203204
}
204205
}

tests/ui/transmutability/alignment/align-fail.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ mod assert {
1818
}
1919

2020
fn main() {
21-
assert::is_maybe_transmutable::<&'static [u8; 0], &'static [u16; 0]>(); //~ ERROR `&[u8; 0]` cannot be safely transmuted into `&[u16; 0]`
21+
assert::is_maybe_transmutable::<&'static [u8; 0], &'static [u16; 0]>(); //~ ERROR `&'static [u8; 0]` cannot be safely transmuted into `&'static [u16; 0]`
2222
}

tests/ui/transmutability/alignment/align-fail.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0277]: `&[u8; 0]` cannot be safely transmuted into `&[u16; 0]`
1+
error[E0277]: `&'static [u8; 0]` cannot be safely transmuted into `&'static [u16; 0]`
22
--> $DIR/align-fail.rs:21:55
33
|
4-
LL | ...tatic [u8; 0], &'static [u16; 0]>();
5-
| ^^^^^^^^^^^^^^^^^ the minimum alignment of `&[u8; 0]` (1) should be greater than that of `&[u16; 0]` (2)
4+
LL | ...8; 0], &'static [u16; 0]>();
5+
| ^^^^^^^^^^^^^^^^^ the minimum alignment of `&'static [u8; 0]` (1) should be greater than that of `&'static [u16; 0]` (2)
66
|
77
note: required by a bound in `is_maybe_transmutable`
88
--> $DIR/align-fail.rs:9:14
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![crate_type = "lib"]
2+
#![feature(transmutability)]
3+
trait Aaa {
4+
type Y;
5+
}
6+
7+
trait Bbb {
8+
type B: std::mem::TransmuteFrom<()>;
9+
}
10+
11+
impl<T> Bbb for T
12+
where
13+
T: Aaa,
14+
{
15+
type B = T::Y; //~ERROR: `()` cannot be safely transmuted into `<T as Aaa>::Y`
16+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0277]: `()` cannot be safely transmuted into `<T as Aaa>::Y`
2+
--> $DIR/assoc-bound.rs:15:14
3+
|
4+
LL | type B = T::Y;
5+
| ^^^^ `<T as Aaa>::Y` has an unknown layout
6+
|
7+
note: required by a bound in `Bbb::B`
8+
--> $DIR/assoc-bound.rs:8:13
9+
|
10+
LL | type B: std::mem::TransmuteFrom<()>;
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Bbb::B`
12+
help: consider further restricting the associated type
13+
|
14+
LL | T: Aaa, <T as Aaa>::Y: TransmuteFrom<(), Assume { alignment: false, lifetimes: false, safety: false, validity: false }>
15+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16+
17+
error: aborting due to 1 previous error
18+
19+
For more information about this error, try `rustc --explain E0277`.

tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible-mut.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0277]: `&A` cannot be safely transmuted into `&mut B`
1+
error[E0277]: `&'static A` cannot be safely transmuted into `&'static mut B`
22
--> $DIR/recursive-wrapper-types-bit-compatible-mut.rs:23:49
33
|
44
LL | assert::is_maybe_transmutable::<&'static A, &'static mut B>();
5-
| ^^^^^^^^^^^^^^ `&A` is a shared reference, but `&mut B` is a unique reference
5+
| ^^^^^^^^^^^^^^ `&'static A` is a shared reference, but `&'static mut B` is a unique reference
66
|
77
note: required by a bound in `is_maybe_transmutable`
88
--> $DIR/recursive-wrapper-types-bit-compatible-mut.rs:9:14

tests/ui/transmutability/references/unit-to-u8.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0277]: `&Unit` cannot be safely transmuted into `&u8`
1+
error[E0277]: `&'static Unit` cannot be safely transmuted into `&'static u8`
22
--> $DIR/unit-to-u8.rs:22:52
33
|
44
LL | assert::is_maybe_transmutable::<&'static Unit, &'static u8>();

tests/ui/transmutability/references/unsafecell.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0277]: `&u8` cannot be safely transmuted into `&UnsafeCell<u8>`
1+
error[E0277]: `&'static u8` cannot be safely transmuted into `&'static UnsafeCell<u8>`
22
--> $DIR/unsafecell.rs:27:50
33
|
44
LL | assert::is_maybe_transmutable::<&'static u8, &'static UnsafeCell<u8>>();
@@ -13,7 +13,7 @@ LL | where
1313
LL | Dst: TransmuteFrom<Src, { Assume::SAFETY }>
1414
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
1515

16-
error[E0277]: `&UnsafeCell<u8>` cannot be safely transmuted into `&UnsafeCell<u8>`
16+
error[E0277]: `&'static UnsafeCell<u8>` cannot be safely transmuted into `&'static UnsafeCell<u8>`
1717
--> $DIR/unsafecell.rs:29:62
1818
|
1919
LL | assert::is_maybe_transmutable::<&'static UnsafeCell<u8>, &'static UnsafeCell<u8>>();

0 commit comments

Comments
 (0)