about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-08-12 10:02:45 +0000
committerbors <bors@rust-lang.org>2023-08-12 10:02:45 +0000
commitf1b854818db00bec14accbc9d1c72e6ebefe64db (patch)
treef4de2918d416f703cf171279178ff64c575b1336
parentb08dd92552d663e3c877c8e5ce859e212205a09f (diff)
parent3028dc4ef7719178b2567e92d57d893be1b45176 (diff)
downloadrust-f1b854818db00bec14accbc9d1c72e6ebefe64db.tar.gz
rust-f1b854818db00bec14accbc9d1c72e6ebefe64db.zip
Auto merge of #109356 - jackh726:issue-108544, r=lcnr
Only check outlives goals on impl compared to trait

Fixes #108544

r? `@compiler-errors`
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs51
-rw-r--r--tests/ui/implied-bounds/implied_bounds_entailment_skip_non_outlives.rs23
-rw-r--r--tests/ui/implied-bounds/trait-where-clause-implied.rs15
3 files changed, 77 insertions, 12 deletions
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 a8c66ff9001..044fb405e32 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 00000000000..8dcc35a281a
--- /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<Cow<'static, str>>
+    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<Cow<'static, str>> {
+        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 00000000000..5f9ab66d3c8
--- /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() {}