Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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`.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /