about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2025-01-22 19:29:43 +0100
committerGitHub <noreply@github.com>2025-01-22 19:29:43 +0100
commit318466aec070f9200a89059e170f5988c6575600 (patch)
treeb9698b915b7d9405dded2779995feb3d414f8f49 /compiler
parent413f87a1f875e600579259e99b6133c4db81033f (diff)
parent3ef506fb4dc9415620d4f09be022834d02201257 (diff)
downloadrust-318466aec070f9200a89059e170f5988c6575600.tar.gz
rust-318466aec070f9200a89059e170f5988c6575600.zip
Rollup merge of #135866 - BoxyUwU:dont_pick_fnptr_nested_goals, r=lcnr
Don't pick `T: FnPtr` nested goals as the leaf goal in diagnostics for new solver

r? `@lcnr`

See `tests/ui/traits/next-solver/diagnostics/dont-pick-fnptr-bound-as-leaf.rs` for a minimized example of what code this affects the diagnostics off. The output of running nightly `-Znext-solver` on that test is the following:
```
error[E0277]: the trait bound `Foo: Trait` is not satisfied
  --> src/lib.rs:14:20
   |
14 |     requires_trait(Foo);
   |     -------------- ^^^ the trait `FnPtr` is not implemented for `Foo`
   |     |
   |     required by a bound introduced by this call
   |
note: required for `Foo` to implement `Trait`
  --> src/lib.rs:7:16
   |
7  | impl<T: FnPtr> Trait for T {}
   |         -----  ^^^^^     ^
   |         |
   |         unsatisfied trait bound introduced here
note: required by a bound in `requires_trait`
  --> src/lib.rs:11:22
   |
11 | fn requires_trait<T: Trait>(_: T) {}
   |                      ^^^^^ required by this bound in `requires_trait`
```

Part of rust-lang/trait-system-refactor-initiative#148
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs23
1 files changed, 21 insertions, 2 deletions
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 4498beff4ea..2b7da4bc5ff 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -513,8 +513,27 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
             _ => ChildMode::PassThrough,
         };
 
+        let nested_goals = candidate.instantiate_nested_goals(self.span());
+
+        // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
+        // an actual candidate, instead we should treat them as if the impl was never considered to
+        // have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written
+        // instead of `impl<T: FnPtr> Trait for T`.
+        //
+        // We do this as a separate loop so that we do not choose to tell the user about some nested
+        // goal before we encounter a `T: FnPtr` nested goal.
+        for nested_goal in &nested_goals {
+            if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait()
+                && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
+                && poly_trait_pred.def_id() == fn_ptr_trait
+                && let Err(NoSolution) = nested_goal.result()
+            {
+                return ControlFlow::Break(self.obligation.clone());
+            }
+        }
+
         let mut impl_where_bound_count = 0;
-        for nested_goal in candidate.instantiate_nested_goals(self.span()) {
+        for nested_goal in nested_goals {
             trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
 
             let make_obligation = |cause| Obligation {
@@ -605,7 +624,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
     }
 }
 
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
 enum ChildMode<'tcx> {
     // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
     // and skip all `GoalSource::Misc`, which represent useless obligations