diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index a8c66ff900116be79b60f0d5dc056fb4db01ce82..044fb405e321901f5499d448f633cd9b9443f9d3 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1,7 +1,7 @@ use super::potentially_plural_count; use crate::errors::LifetimesOrBoundsMismatchOnTrait; use hir::def_id::{DefId, LocalDefId}; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_errors::{ pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed, MultiSpan, }; @@ -265,7 +265,6 @@ fn compare_method_predicate_entailment<'tcx>( infer::HigherRankedType, tcx.fn_sig(impl_m.def_id).instantiate_identity(), ); - let unnormalized_impl_fty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(unnormalized_impl_sig)); let norm_cause = ObligationCause::misc(impl_m_span, impl_m_def_id); let impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_impl_sig); @@ -309,16 +308,44 @@ fn compare_method_predicate_entailment<'tcx>( } if check_implied_wf == CheckImpliedWfMode::Check && !(impl_sig, trait_sig).references_error() { - // We need to check that the impl's args are well-formed given - // the hybrid param-env (impl + trait method where-clauses). - ocx.register_obligation(traits::Obligation::new( - infcx.tcx, - ObligationCause::dummy(), - param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - unnormalized_impl_fty.into(), - ))), - )); + // See #108544. Annoying, we can end up in cases where, because of winnowing, + // we pick param env candidates over a more general impl, leading to more + // stricter lifetime requirements than we would otherwise need. This can + // trigger the lint. Instead, let's only consider type outlives and + // region outlives obligations. + // + // FIXME(-Ztrait-solver=next): Try removing this hack again once + // the new solver is stable. + let mut wf_args: smallvec::SmallVec<[_; 4]> = + unnormalized_impl_sig.inputs_and_output.iter().map(|ty| ty.into()).collect(); + // Annoyingly, asking for the WF predicates of an array (with an unevaluated const (only?)) + // will give back the well-formed predicate of the same array. + let mut wf_args_seen: FxHashSet<_> = wf_args.iter().copied().collect(); + while let Some(arg) = wf_args.pop() { + let Some(obligations) = rustc_trait_selection::traits::wf::obligations( + infcx, + param_env, + impl_m_def_id, + 0, + arg, + impl_m_span, + ) else { + continue; + }; + for obligation in obligations { + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause( + ty::ClauseKind::RegionOutlives(..) | ty::ClauseKind::TypeOutlives(..), + ) => ocx.register_obligation(obligation), + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + if wf_args_seen.insert(arg) { + wf_args.push(arg) + } + } + _ => {} + } + } + } } // Check that all obligations are satisfied by the implementation's diff --git a/tests/ui/implied-bounds/implied_bounds_entailment_skip_non_outlives.rs b/tests/ui/implied-bounds/implied_bounds_entailment_skip_non_outlives.rs new file mode 100644 index 0000000000000000000000000000000000000000..8dcc35a281a7725e7af9822d396fbad25ef6f7f6 --- /dev/null +++ b/tests/ui/implied-bounds/implied_bounds_entailment_skip_non_outlives.rs @@ -0,0 +1,23 @@ +// check-pass +// See issue #109356. We don't want a false positive to the `implied_bounds_entailment` lint. + +use std::borrow::Cow; + +pub trait Trait { + fn method(self) -> Option> + where + Self: Sized; +} + +impl<'a> Trait for Cow<'a, str> { + // If we're not careful here, we'll check `WF(return-type)` using the trait + // and impl where clauses, requiring that `Cow<'a, str>: Sized`. This is + // obviously true, but if we pick the `Self: Sized` clause from the trait + // over the "inherent impl", we will require `'a == 'static`, which triggers + // the `implied_bounds_entailment` lint. + fn method(self) -> Option> { + None + } +} + +fn main() {} diff --git a/tests/ui/implied-bounds/trait-where-clause-implied.rs b/tests/ui/implied-bounds/trait-where-clause-implied.rs new file mode 100644 index 0000000000000000000000000000000000000000..5f9ab66d3c896e9bbb69ecf5a0dec1277b8dc914 --- /dev/null +++ b/tests/ui/implied-bounds/trait-where-clause-implied.rs @@ -0,0 +1,15 @@ +// check-pass + +pub trait Trait<'a, 'b> { + fn method(self, _: &'static &'static ()) + where + 'a: 'b; +} + +impl<'a> Trait<'a, 'static> for () { + // On first glance, this seems like we have the extra implied bound that + // `'a: 'static`, but we know this from the trait method where clause. + fn method(self, _: &'static &'a ()) {} +} + +fn main() {}