diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src')
8 files changed, 132 insertions, 89 deletions
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index 248985715c2..ed839d14dc7 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -166,13 +166,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; if !drcx.args_may_unify( goal.predicate.trait_ref(tcx).args, - impl_trait_header.skip_binder().trait_ref.args, + impl_trait_header.trait_ref.skip_binder().args, ) { return Err(NoSolution); } // We have to ignore negative impls when projecting. - let impl_polarity = impl_trait_header.skip_binder().polarity; + let impl_polarity = impl_trait_header.polarity; match impl_polarity { ty::ImplPolarity::Negative => return Err(NoSolution), ty::ImplPolarity::Reservation => { @@ -183,7 +183,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { let impl_args = ecx.fresh_args_for_item(impl_def_id); - let impl_trait_ref = impl_trait_header.instantiate(tcx, impl_args).trait_ref; + let impl_trait_ref = impl_trait_header.trait_ref.instantiate(tcx, impl_args); ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?; diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 16febf05b60..281f5cc5685 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -47,14 +47,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; if !drcx.args_may_unify( goal.predicate.trait_ref.args, - impl_trait_header.skip_binder().trait_ref.args, + impl_trait_header.trait_ref.skip_binder().args, ) { return Err(NoSolution); } // An upper bound of the certainty of this goal, used to lower the certainty // of reservation impl to ambiguous during coherence. - let impl_polarity = impl_trait_header.skip_binder().polarity; + let impl_polarity = impl_trait_header.polarity; let maximal_certainty = match impl_polarity { ty::ImplPolarity::Positive | ty::ImplPolarity::Negative => { match impl_polarity == goal.predicate.polarity { @@ -70,7 +70,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { let impl_args = ecx.fresh_args_for_item(impl_def_id); - let impl_trait_ref = impl_trait_header.instantiate(tcx, impl_args).trait_ref; + let impl_trait_ref = impl_trait_header.trait_ref.instantiate(tcx, impl_args); ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?; let where_clause_bounds = tcx diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 7a930937255..ac2b738d3b6 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -1431,45 +1431,64 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { #[extension(pub(super) trait InferCtxtPrivExt<'tcx>)] impl<'tcx> TypeErrCtxt<'_, 'tcx> { + fn can_match_trait( + &self, + goal: ty::TraitPredicate<'tcx>, + assumption: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + if goal.polarity != assumption.polarity() { + return false; + } + + let trait_goal = goal.trait_ref; + let trait_assumption = self.instantiate_binder_with_fresh_vars( + DUMMY_SP, + infer::BoundRegionConversionTime::HigherRankedType, + assumption.to_poly_trait_ref(), + ); + + self.can_eq(ty::ParamEnv::empty(), trait_goal, trait_assumption) + } + + fn can_match_projection( + &self, + goal: ty::ProjectionPredicate<'tcx>, + assumption: ty::PolyProjectionPredicate<'tcx>, + ) -> bool { + let assumption = self.instantiate_binder_with_fresh_vars( + DUMMY_SP, + infer::BoundRegionConversionTime::HigherRankedType, + assumption, + ); + + let param_env = ty::ParamEnv::empty(); + self.can_eq(param_env, goal.projection_ty, assumption.projection_ty) + && self.can_eq(param_env, goal.term, assumption.term) + } + // returns if `cond` not occurring implies that `error` does not occur - i.e., that // `error` occurring implies that `cond` occurs. + #[instrument(level = "debug", skip(self), ret)] fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool { if cond == error { return true; } - // FIXME: It should be possible to deal with `ForAll` in a cleaner way. - let bound_error = error.kind(); - let (cond, error) = match (cond.kind().skip_binder(), bound_error.skip_binder()) { - ( - ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)), - ty::PredicateKind::Clause(ty::ClauseKind::Trait(error)), - ) => (cond, bound_error.rebind(error)), - _ => { - // FIXME: make this work in other cases too. - return false; - } - }; - - for pred in elaborate(self.tcx, std::iter::once(cond)) { - let bound_predicate = pred.kind(); - if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(implication)) = - bound_predicate.skip_binder() - { - let error = error.to_poly_trait_ref(); - let implication = bound_predicate.rebind(implication.trait_ref); - // FIXME: I'm just not taking associated types at all here. - // Eventually I'll need to implement param-env-aware - // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic. - let param_env = ty::ParamEnv::empty(); - if self.can_sub(param_env, error, implication) { - debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication); - return true; - } - } + if let Some(error) = error.to_opt_poly_trait_pred() { + self.enter_forall(error, |error| { + elaborate(self.tcx, std::iter::once(cond)) + .filter_map(|implied| implied.to_opt_poly_trait_pred()) + .any(|implied| self.can_match_trait(error, implied)) + }) + } else if let Some(error) = error.to_opt_poly_projection_pred() { + self.enter_forall(error, |error| { + elaborate(self.tcx, std::iter::once(cond)) + .filter_map(|implied| implied.to_opt_poly_projection_pred()) + .any(|implied| self.can_match_projection(error, implied)) + }) + } else { + false } - - false } #[instrument(skip(self), level = "debug")] @@ -1888,13 +1907,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .tcx .all_impls(trait_pred.def_id()) .filter_map(|def_id| { - let imp = self.tcx.impl_trait_header(def_id).unwrap().skip_binder(); + let imp = self.tcx.impl_trait_header(def_id).unwrap(); if imp.polarity == ty::ImplPolarity::Negative || !self.tcx.is_user_visible_dep(def_id.krate) { return None; } - let imp = imp.trait_ref; + let imp = imp.trait_ref.skip_binder(); self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false).map( |similarity| ImplCandidate { trait_ref: imp, similarity, impl_def_id: def_id }, @@ -2078,12 +2097,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .all_impls(def_id) // Ignore automatically derived impls and `!Trait` impls. .filter_map(|def_id| self.tcx.impl_trait_header(def_id)) - .map(ty::EarlyBinder::instantiate_identity) - .filter(|header| { - header.polarity != ty::ImplPolarity::Negative - || self.tcx.is_automatically_derived(def_id) + .filter_map(|header| { + (header.polarity != ty::ImplPolarity::Negative + || self.tcx.is_automatically_derived(def_id)) + .then(|| header.trait_ref.instantiate_identity()) }) - .map(|header| header.trait_ref) .filter(|trait_ref| { let self_ty = trait_ref.self_ty(); // Avoid mentioning type parameters. diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 39f4ceda9f1..66f740b761d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -562,7 +562,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // and so forth that we need to. let impl_trait_header = self.tcx().impl_trait_header(impl_def_id).unwrap(); if !drcx - .args_may_unify(obligation_args, impl_trait_header.skip_binder().trait_ref.args) + .args_may_unify(obligation_args, impl_trait_header.trait_ref.skip_binder().args) { return; } @@ -577,7 +577,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if self.reject_fn_ptr_impls( impl_def_id, obligation, - impl_trait_header.skip_binder().trait_ref.self_ty(), + impl_trait_header.trait_ref.skip_binder().self_ty(), ) { return; } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 7dbea0cdb90..a6bd1ba9c3f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -42,7 +42,7 @@ use rustc_middle::ty::_match::MatchAgainstFreshVars; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; +use rustc_middle::ty::{self, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::Symbol; @@ -2441,7 +2441,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { fn match_impl( &mut self, impl_def_id: DefId, - impl_trait_header: EarlyBinder<ty::ImplTraitHeader<'tcx>>, + impl_trait_header: ty::ImplTraitHeader<'tcx>, obligation: &PolyTraitObligation<'tcx>, ) -> Result<Normalized<'tcx, GenericArgsRef<'tcx>>, ()> { let placeholder_obligation = @@ -2450,8 +2450,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let impl_args = self.infcx.fresh_args_for_item(obligation.cause.span, impl_def_id); - let impl_trait_header = impl_trait_header.instantiate(self.tcx(), impl_args); - if impl_trait_header.references_error() { + let trait_ref = impl_trait_header.trait_ref.instantiate(self.tcx(), impl_args); + if trait_ref.references_error() { return Err(()); } @@ -2464,7 +2464,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, - impl_trait_header.trait_ref, + trait_ref, ) }); diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index f5bc6c3ad2c..27dd8f26489 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -169,7 +169,7 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, } } - let impl1_trait_header = tcx.impl_trait_header(impl1_def_id).unwrap().instantiate_identity(); + let impl1_trait_header = tcx.impl_trait_header(impl1_def_id).unwrap(); // We determine whether there's a subset relationship by: // @@ -198,7 +198,7 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, fulfill_implication( &infcx, penv, - impl1_trait_header.trait_ref, + impl1_trait_header.trait_ref.instantiate_identity(), impl1_def_id, impl2_def_id, |_, _| ObligationCause::dummy(), diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 6e01e0b76aa..3f433a9e919 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -127,7 +127,7 @@ impl<'tcx> TraitAliasExpander<'tcx> { } // Get components of trait alias. - let predicates = tcx.implied_predicates_of(trait_ref.def_id()); + let predicates = tcx.super_predicates_of(trait_ref.def_id()); debug!(?predicates); let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| { diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 15059bc6613..b09a803e856 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -223,60 +223,87 @@ enum Elaborate { None, } +/// Points the cause span of a super predicate at the relevant associated type. +/// +/// Given a trait impl item: +/// +/// ```ignore (incomplete) +/// impl TargetTrait for TargetType { +/// type Assoc = SomeType; +/// } +/// ``` +/// +/// And a super predicate of `TargetTrait` that has any of the following forms: +/// +/// 1. `<OtherType as OtherTrait>::Assoc == <TargetType as TargetTrait>::Assoc` +/// 2. `<<TargetType as TargetTrait>::Assoc as OtherTrait>::Assoc == OtherType` +/// 3. `<TargetType as TargetTrait>::Assoc: OtherTrait` +/// +/// Replace the span of the cause with the span of the associated item: +/// +/// ```ignore (incomplete) +/// impl TargetTrait for TargetType { +/// type Assoc = SomeType; +/// // ^^^^^^^^ this span +/// } +/// ``` +/// +/// Note that bounds that can be expressed as associated item bounds are **not** +/// super predicates. This means that form 2 and 3 from above are only relevant if +/// the [`GenericArgsRef`] of the projection type are not its identity arguments. fn extend_cause_with_original_assoc_item_obligation<'tcx>( tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, item: Option<&hir::Item<'tcx>>, cause: &mut traits::ObligationCause<'tcx>, pred: ty::Predicate<'tcx>, ) { - debug!( - "extended_cause_with_original_assoc_item_obligation {:?} {:?} {:?} {:?}", - trait_ref, item, cause, pred - ); + debug!(?item, ?cause, ?pred, "extended_cause_with_original_assoc_item_obligation"); let (items, impl_def_id) = match item { Some(hir::Item { kind: hir::ItemKind::Impl(impl_), owner_id, .. }) => { (impl_.items, *owner_id) } _ => return, }; - let fix_span = - |impl_item_ref: &hir::ImplItemRef| match tcx.hir().impl_item(impl_item_ref.id).kind { - hir::ImplItemKind::Const(ty, _) | hir::ImplItemKind::Type(ty) => ty.span, - _ => impl_item_ref.span, - }; + + let ty_to_impl_span = |ty: Ty<'_>| { + if let ty::Alias(ty::Projection, projection_ty) = ty.kind() + && let Some(&impl_item_id) = + tcx.impl_item_implementor_ids(impl_def_id).get(&projection_ty.def_id) + && let Some(impl_item) = + items.iter().find(|item| item.id.owner_id.to_def_id() == impl_item_id) + { + Some(tcx.hir().impl_item(impl_item.id).expect_type().span) + } else { + None + } + }; // It is fine to skip the binder as we don't care about regions here. match pred.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) => { - // The obligation comes not from the current `impl` nor the `trait` being implemented, - // but rather from a "second order" obligation, where an associated type has a - // projection coming from another associated type. See - // `tests/ui/associated-types/point-at-type-on-obligation-failure.rs` and - // `traits-assoc-type-in-supertrait-bad.rs`. - if let Some(ty::Alias(ty::Projection, projection_ty)) = - proj.term.ty().map(|ty| ty.kind()) - && let Some(&impl_item_id) = - tcx.impl_item_implementor_ids(impl_def_id).get(&projection_ty.def_id) - && let Some(impl_item_span) = items - .iter() - .find(|item| item.id.owner_id.to_def_id() == impl_item_id) - .map(fix_span) + // Form 1: The obligation comes not from the current `impl` nor the `trait` being + // implemented, but rather from a "second order" obligation, where an associated + // type has a projection coming from another associated type. + // See `tests/ui/traits/assoc-type-in-superbad.rs` for an example. + if let Some(term_ty) = proj.term.ty() + && let Some(impl_item_span) = ty_to_impl_span(term_ty) { cause.span = impl_item_span; } + + // Form 2: A projection obligation for an associated item failed to be met. + // We overwrite the span from above to ensure that a bound like + // `Self::Assoc1: Trait<OtherAssoc = Self::Assoc2>` gets the same + // span for both obligations that it is lowered to. + if let Some(impl_item_span) = ty_to_impl_span(proj.self_ty()) { + cause.span = impl_item_span; + } } + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { - // An associated item obligation born out of the `trait` failed to be met. An example - // can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`. + // Form 3: A trait obligation for an associated item failed to be met. debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred); - if let ty::Alias(ty::Projection, ty::AliasTy { def_id, .. }) = *pred.self_ty().kind() - && let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&def_id) - && let Some(impl_item_span) = items - .iter() - .find(|item| item.id.owner_id.to_def_id() == impl_item_id) - .map(fix_span) - { + if let Some(impl_item_span) = ty_to_impl_span(pred.self_ty()) { cause.span = impl_item_span; } } @@ -355,9 +382,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { traits::ObligationCauseCode::DerivedObligation, ); } - extend_cause_with_original_assoc_item_obligation( - tcx, trait_ref, item, &mut cause, predicate, - ); + extend_cause_with_original_assoc_item_obligation(tcx, item, &mut cause, predicate); traits::Obligation::with_depth(tcx, cause, depth, param_env, predicate) }; |
