|
| 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 | +} |
0 commit comments