diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 7e03ef1814402..0b4df8e6d3cdd 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -42,6 +42,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; +use rustc_infer::traits::Obligation; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, @@ -50,7 +51,7 @@ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Ty, TypeAndMut}; +use rustc_middle::ty::{self, ToPredicate, Ty, TypeAndMut}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{self, BytePos, Span}; @@ -172,7 +173,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { kind: TypeVariableOriginKind::AdjustmentType, span: self.cause.span, }); - self.unify_and(&b, &diverging_ty, simple(Adjust::NeverToAny)) + self.coerce_from_inference_variable(diverging_ty, b, simple(Adjust::NeverToAny)) } else { success(simple(Adjust::NeverToAny)(b), b, vec![]) }; @@ -182,7 +183,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // we have no information about the source type. This will always // ultimately fall back to some form of subtyping. if a.is_ty_var() { - return self.coerce_from_inference_variable(a, b); + return self.coerce_from_inference_variable(a, b, identity); } // Consider coercing the subtype to a DST @@ -245,11 +246,53 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { /// Coercing *from* an inference variable. In this case, we have no information /// about the source type, so we can't really do a true coercion and we always /// fall back to subtyping (`unify_and`). - fn coerce_from_inference_variable(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + fn coerce_from_inference_variable( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + make_adjustments: impl FnOnce(Ty<'tcx>) -> Vec>, + ) -> CoerceResult<'tcx> { + debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b); assert!(a.is_ty_var() && self.infcx.shallow_resolve(a) == a); assert!(self.infcx.shallow_resolve(b) == b); - self.unify_and(a, b, identity) + if b.is_ty_var() { + // Two unresolved type variables: create a `Coerce` predicate. + let target_ty = if self.use_lub { + self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::LatticeVariable, + span: self.cause.span, + }) + } else { + b + }; + + let mut obligations = Vec::with_capacity(2); + for &source_ty in &[a, b] { + if source_ty != target_ty { + obligations.push(Obligation::new( + self.cause.clone(), + self.param_env, + ty::PredicateKind::Coerce(ty::CoercePredicate { + a: source_ty, + b: target_ty, + }) + .to_predicate(self.tcx()), + )); + } + } + + debug!( + "coerce_from_inference_variable: two inference variables, target_ty={:?}, obligations={:?}", + target_ty, obligations + ); + let adjustments = make_adjustments(target_ty); + InferResult::Ok(InferOk { value: (adjustments, target_ty), obligations }) + } else { + // One unresolved type variable: just apply subtyping, we may be able + // to do something useful. + self.unify_and(a, b, make_adjustments) + } } /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.