Skip to content

Commit f4cf8f6

Browse files
committed
Safe Transmute: Refactor error handling and Answer type
- Create `Answer` type that is not just a type alias of `Result` - Remove a usage of `map_layouts` to make the code easier to read - Don't hide errors related to Unknown Layout when computing transmutability
1 parent 64a54df commit f4cf8f6

File tree

9 files changed

+167
-127
lines changed

9 files changed

+167
-127
lines changed

compiler/rustc_trait_selection/src/solve/eval_ctxt.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -668,18 +668,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
668668
scope: Ty<'tcx>,
669669
assume: rustc_transmute::Assume,
670670
) -> Result<Certainty, NoSolution> {
671+
use rustc_transmute::Answer;
671672
// FIXME(transmutability): This really should be returning nested goals for `Answer::If*`
672673
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
673674
ObligationCause::dummy(),
674675
src_and_dst,
675676
scope,
676677
assume,
677678
) {
678-
Ok(None) => Ok(Certainty::Yes),
679-
Err(_)
680-
| Ok(Some(rustc_transmute::Condition::IfTransmutable { .. }))
681-
| Ok(Some(rustc_transmute::Condition::IfAll(_)))
682-
| Ok(Some(rustc_transmute::Condition::IfAny(_))) => Err(NoSolution),
679+
Answer::Yes => Ok(Certainty::Yes),
680+
Answer::No(_) | Answer::If(_) => Err(NoSolution),
683681
}
684682
}
685683
}

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

+32-9
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ pub struct ImplCandidate<'tcx> {
6565
pub similarity: CandidateSimilarity,
6666
}
6767

68+
enum GetSafeTransmuteErrorAndReason {
69+
Silent,
70+
Error { err_msg: String, safe_transmute_explanation: String },
71+
}
72+
6873
pub trait InferCtxtExt<'tcx> {
6974
/// Given some node representing a fn-like thing in the HIR map,
7075
/// returns a span and `ArgKind` information that describes the
@@ -724,11 +729,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
724729
== self.tcx.lang_items().transmute_trait()
725730
{
726731
// Recompute the safe transmute reason and use that for the error reporting
727-
self.get_safe_transmute_error_and_reason(
732+
match self.get_safe_transmute_error_and_reason(
728733
obligation.clone(),
729734
trait_ref,
730735
span,
731-
)
736+
) {
737+
GetSafeTransmuteErrorAndReason::Silent => return,
738+
GetSafeTransmuteErrorAndReason::Error {
739+
err_msg,
740+
safe_transmute_explanation,
741+
} => (err_msg, Some(safe_transmute_explanation)),
742+
}
732743
} else {
733744
(err_msg, None)
734745
};
@@ -1292,7 +1303,7 @@ trait InferCtxtPrivExt<'tcx> {
12921303
obligation: PredicateObligation<'tcx>,
12931304
trait_ref: ty::PolyTraitRef<'tcx>,
12941305
span: Span,
1295-
) -> (String, Option<String>);
1306+
) -> GetSafeTransmuteErrorAndReason;
12961307

12971308
fn add_tuple_trait_message(
12981309
&self,
@@ -2738,7 +2749,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
27382749
obligation: PredicateObligation<'tcx>,
27392750
trait_ref: ty::PolyTraitRef<'tcx>,
27402751
span: Span,
2741-
) -> (String, Option<String>) {
2752+
) -> GetSafeTransmuteErrorAndReason {
2753+
use rustc_transmute::Answer;
2754+
27422755
// Erase regions because layout code doesn't particularly care about regions.
27432756
let trait_ref = self.tcx.erase_regions(self.tcx.erase_late_bound_regions(trait_ref));
27442757

@@ -2758,13 +2771,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
27582771
scope,
27592772
assume,
27602773
) {
2761-
Err(reason) => {
2774+
Answer::No(reason) => {
27622775
let dst = trait_ref.substs.type_at(0);
27632776
let src = trait_ref.substs.type_at(1);
2764-
let custom_err_msg = format!(
2777+
let err_msg = format!(
27652778
"`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`"
27662779
);
2767-
let reason_msg = match reason {
2780+
let safe_transmute_explanation = match reason {
27682781
rustc_transmute::Reason::SrcIsUnspecified => {
27692782
format!("`{src}` does not have a well-specified layout")
27702783
}
@@ -2794,11 +2807,21 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
27942807
rustc_transmute::Reason::DstIsMoreUnique => {
27952808
format!("`{src}` is a shared reference, but `{dst}` is a unique reference")
27962809
}
2810+
// Already reported by rustc
2811+
rustc_transmute::Reason::TypeError => {
2812+
return GetSafeTransmuteErrorAndReason::Silent;
2813+
}
2814+
rustc_transmute::Reason::SrcLayoutUnknown => {
2815+
format!("`{src}` has an unknown layout")
2816+
}
2817+
rustc_transmute::Reason::DstLayoutUnknown => {
2818+
format!("`{dst}` has an unknown layout")
2819+
}
27972820
};
2798-
(custom_err_msg, Some(reason_msg))
2821+
GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation }
27992822
}
28002823
// Should never get a Yes at this point! We already ran it before, and did not get a Yes.
2801-
Ok(None) => span_bug!(
2824+
Answer::Yes => span_bug!(
28022825
span,
28032826
"Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
28042827
),

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

+14-21
Original file line numberDiff line numberDiff line change
@@ -285,28 +285,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
285285
&mut self,
286286
obligation: &TraitObligation<'tcx>,
287287
) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
288+
use rustc_transmute::{Answer, Condition};
288289
#[instrument(level = "debug", skip(tcx, obligation, predicate))]
289290
fn flatten_answer_tree<'tcx>(
290291
tcx: TyCtxt<'tcx>,
291292
obligation: &TraitObligation<'tcx>,
292293
predicate: TraitPredicate<'tcx>,
293-
answer: rustc_transmute::Condition<rustc_transmute::layout::rustc::Ref<'tcx>>,
294+
cond: Condition<rustc_transmute::layout::rustc::Ref<'tcx>>,
294295
) -> Vec<PredicateObligation<'tcx>> {
295-
match answer {
296+
match cond {
296297
// FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
297298
// Not possible until the trait solver supports disjunctions of obligations
298-
rustc_transmute::Condition::IfAll(answers)
299-
| rustc_transmute::Condition::IfAny(answers) => {
300-
let mut nested = vec![];
301-
for flattened in answers
302-
.into_iter()
303-
.map(|answer| flatten_answer_tree(tcx, obligation, predicate, answer))
304-
{
305-
nested.extend(flattened);
306-
}
307-
nested
308-
}
309-
rustc_transmute::Condition::IfTransmutable { src, dst } => {
299+
Condition::IfAll(conds) | Condition::IfAny(conds) => conds
300+
.into_iter()
301+
.flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond))
302+
.collect(),
303+
Condition::IfTransmutable { src, dst } => {
310304
let trait_def_id = obligation.predicate.def_id();
311305
let scope = predicate.trait_ref.substs.type_at(2);
312306
let assume_const = predicate.trait_ref.substs.const_at(3);
@@ -333,11 +327,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
333327
// If Dst is mutable, check bidirectionally.
334328
// For example, transmuting bool -> u8 is OK as long as you can't update that u8
335329
// to be > 1, because you could later transmute the u8 back to a bool and get UB.
336-
let mut obligations = vec![make_obl(src.ty, dst.ty)];
337-
if dst.mutability == Mutability::Mut {
338-
obligations.push(make_obl(dst.ty, src.ty));
330+
match dst.mutability {
331+
Mutability::Not => vec![make_obl(src.ty, dst.ty)],
332+
Mutability::Mut => vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)],
339333
}
340-
obligations
341334
}
342335
}
343336
}
@@ -370,9 +363,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
370363
);
371364

372365
let fully_flattened = match maybe_transmutable {
373-
Err(_) => Err(Unimplemented)?,
374-
Ok(Some(mt)) => flatten_answer_tree(self.tcx(), obligation, predicate, mt),
375-
Ok(None) => vec![],
366+
Answer::No(_) => Err(Unimplemented)?,
367+
Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, predicate, cond),
368+
Answer::Yes => vec![],
376369
};
377370

378371
debug!(?fully_flattened);

compiler/rustc_transmute/src/layout/tree.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,14 @@ pub(crate) mod rustc {
188188
/// The layout of the type is unspecified.
189189
Unspecified,
190190
/// This error will be surfaced elsewhere by rustc, so don't surface it.
191-
Unknown,
191+
UnknownLayout,
192192
TypeError(ErrorGuaranteed),
193193
}
194194

195195
impl<'tcx> From<LayoutError<'tcx>> for Err {
196196
fn from(err: LayoutError<'tcx>) -> Self {
197197
match err {
198-
LayoutError::Unknown(..) => Self::Unknown,
198+
LayoutError::Unknown(..) => Self::UnknownLayout,
199199
err => unimplemented!("{:?}", err),
200200
}
201201
}

compiler/rustc_transmute/src/lib.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,16 @@ pub struct Assume {
1919
pub validity: bool,
2020
}
2121

22-
/// Either we have an error, or we have an optional Condition that must hold.
23-
pub type Answer<R> = Result<Option<Condition<R>>, Reason>;
22+
/// Either we have an error, transmutation is allowed, or we have an optional
23+
/// Condition that must hold.
24+
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
25+
pub enum Answer<R> {
26+
Yes,
27+
No(Reason),
28+
If(Condition<R>),
29+
}
2430

25-
/// A condition which must hold for safe transmutation to be possible
31+
/// A condition which must hold for safe transmutation to be possible.
2632
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
2733
pub enum Condition<R> {
2834
/// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`.
@@ -35,7 +41,7 @@ pub enum Condition<R> {
3541
IfAny(Vec<Condition<R>>),
3642
}
3743

38-
/// Answers: Why wasn't the source type transmutable into the destination type?
44+
/// Answers "why wasn't the source type transmutable into the destination type?"
3945
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
4046
pub enum Reason {
4147
/// The layout of the source type is unspecified.
@@ -52,6 +58,12 @@ pub enum Reason {
5258
DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize },
5359
/// Can't go from shared pointer to unique pointer
5460
DstIsMoreUnique,
61+
/// Encountered a type error
62+
TypeError,
63+
/// The layout of src is unknown
64+
SrcLayoutUnknown,
65+
/// The layout of dst is unknown
66+
DstLayoutUnknown,
5567
}
5668

5769
#[cfg(feature = "rustc")]

0 commit comments

Comments
 (0)