about summary refs log tree commit diff
diff options
context:
space:
mode:
authorlcnr <rust@lcnr.de>2025-02-12 11:41:53 +0100
committerlcnr <rust@lcnr.de>2025-02-13 23:49:09 +0100
commit059288ed442f34c336c759486bfe4373b61288a0 (patch)
treeb8b4b60b84e0cd03ea0a48a6ce4881d434887a5a
parent05bd5ced2d9af5f155ed8a72b5c1399838c1d653 (diff)
downloadrust-059288ed442f34c336c759486bfe4373b61288a0.tar.gz
rust-059288ed442f34c336c759486bfe4373b61288a0.zip
adjust derive_error
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs20
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs4
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs128
-rw-r--r--tests/crashes/134905.rs16
-rw-r--r--tests/ui/auto-traits/assoc-ty.next.stderr25
-rw-r--r--tests/ui/auto-traits/assoc-ty.rs4
-rw-r--r--tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.next.stderr14
-rw-r--r--tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs8
-rw-r--r--tests/ui/specialization/fuzzed/fuzzing-ice-134905.rs22
-rw-r--r--tests/ui/specialization/fuzzed/fuzzing-ice-134905.stderr40
11 files changed, 204 insertions, 79 deletions
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
index 23fe88b1680..b0f59ed1474 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -6,7 +6,6 @@ use derive_where::derive_where;
 use rustc_type_ir::fold::TypeFoldable;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
-use rustc_type_ir::solve::inspect;
 use rustc_type_ir::visit::TypeVisitableExt as _;
 use rustc_type_ir::{self as ty, Interner, TypingMode, Upcast as _, elaborate};
 use tracing::{debug, instrument};
@@ -297,25 +296,6 @@ where
         let Ok(normalized_self_ty) =
             self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
         else {
-            // FIXME: We register a fake candidate when normalization fails so that
-            // we can point at the reason for *why*. I'm tempted to say that this
-            // is the wrong way to do this, though.
-            let result =
-                self.probe(|&result| inspect::ProbeKind::RigidAlias { result }).enter(|this| {
-                    let normalized_ty = this.next_ty_infer();
-                    let alias_relate_goal = Goal::new(
-                        this.cx(),
-                        goal.param_env,
-                        ty::PredicateKind::AliasRelate(
-                            goal.predicate.self_ty().into(),
-                            normalized_ty.into(),
-                            ty::AliasRelationDirection::Equate,
-                        ),
-                    );
-                    this.add_goal(GoalSource::AliasWellFormed, alias_relate_goal);
-                    this.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
-                });
-            assert_eq!(result, Err(NoSolution));
             return vec![];
         };
 
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 1b43820bac0..49fa21e50c0 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -585,6 +585,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => {
                         let ty = self.resolve_vars_if_possible(ty);
                         if self.next_trait_solver() {
+                            if let Err(guar) = ty.error_reported() {
+                                return guar;
+                            }
+
                             // FIXME: we'll need a better message which takes into account
                             // which bounds actually failed to hold.
                             self.dcx().struct_span_err(
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
index 658fb4009d5..e4f250ca4f5 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
@@ -172,8 +172,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             {
                 1
             }
-            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3,
             ty::PredicateKind::Coerce(_) => 2,
+            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3,
             _ => 0,
         });
 
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
index 7364b4aa343..982782bc57c 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
@@ -7,7 +7,7 @@ use rustc_infer::traits::{
     PredicateObligation, SelectionError,
 };
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
 use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _};
 use rustc_type_ir::solve::{Goal, NoSolution};
@@ -139,6 +139,7 @@ pub(super) fn fulfillment_error_for_overflow<'tcx>(
     }
 }
 
+#[instrument(level = "debug", skip(infcx), ret)]
 fn find_best_leaf_obligation<'tcx>(
     infcx: &InferCtxt<'tcx>,
     obligation: &PredicateObligation<'tcx>,
@@ -197,6 +198,9 @@ impl<'tcx> BestObligation<'tcx> {
                 candidates.retain(|candidate| candidate.result().is_ok());
             }
             false => {
+                // We always handle rigid alias candidates separately as we may not add them for
+                // aliases whose trait bound doesn't hold.
+                candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. }));
                 // If we have >1 candidate, one may still be due to "boring" reasons, like
                 // an alias-relate that failed to hold when deeply evaluated. We really
                 // don't care about reasons like this.
@@ -211,23 +215,12 @@ impl<'tcx> BestObligation<'tcx> {
                                             | GoalSource::AliasBoundConstCondition
                                             | GoalSource::InstantiateHigherRanked
                                             | GoalSource::AliasWellFormed
-                                    ) && match (self.consider_ambiguities, nested_goal.result()) {
-                                        (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity)))
-                                        | (false, Err(_)) => true,
-                                        _ => false,
-                                    }
+                                    ) && nested_goal.result().is_err()
                                 },
                             )
                         })
                     });
                 }
-
-                // Prefer a non-rigid candidate if there is one.
-                if candidates.len() > 1 {
-                    candidates.retain(|candidate| {
-                        !matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. })
-                    });
-                }
             }
         }
 
@@ -266,6 +259,90 @@ impl<'tcx> BestObligation<'tcx> {
 
         ControlFlow::Break(self.obligation.clone())
     }
+
+    /// If a normalization of an associated item or a trait goal fails without trying any
+    /// candidates it's likely that normalizing its self type failed. We manually detect
+    /// such cases here.
+    fn detect_error_in_self_ty_normalization(
+        &mut self,
+        goal: &inspect::InspectGoal<'_, 'tcx>,
+        self_ty: Ty<'tcx>,
+    ) -> ControlFlow<PredicateObligation<'tcx>> {
+        assert!(!self.consider_ambiguities);
+        let tcx = goal.infcx().tcx;
+        if let ty::Alias(..) = self_ty.kind() {
+            let infer_term = goal.infcx().next_ty_var(self.obligation.cause.span);
+            let pred = ty::PredicateKind::AliasRelate(
+                self_ty.into(),
+                infer_term.into(),
+                ty::AliasRelationDirection::Equate,
+            );
+            let obligation =
+                Obligation::new(tcx, self.obligation.cause.clone(), goal.goal().param_env, pred);
+            self.with_derived_obligation(obligation, |this| {
+                goal.infcx().visit_proof_tree_at_depth(
+                    goal.goal().with(tcx, pred),
+                    goal.depth() + 1,
+                    this,
+                )
+            })
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+
+    /// It is likely that `NormalizesTo` failed without any applicable candidates
+    /// because the alias is not well-formed.
+    ///
+    /// As we only enter `RigidAlias` candidates if the trait bound of the associated type
+    /// holds, we discard these candidates in `non_trivial_candidates` and always manually
+    /// check this here.
+    fn detect_non_well_formed_assoc_item(
+        &mut self,
+        goal: &inspect::InspectGoal<'_, 'tcx>,
+        alias: ty::AliasTerm<'tcx>,
+    ) -> ControlFlow<PredicateObligation<'tcx>> {
+        let tcx = goal.infcx().tcx;
+        let obligation = Obligation::new(
+            tcx,
+            self.obligation.cause.clone(),
+            goal.goal().param_env,
+            alias.trait_ref(tcx),
+        );
+        self.with_derived_obligation(obligation, |this| {
+            goal.infcx().visit_proof_tree_at_depth(
+                goal.goal().with(tcx, alias.trait_ref(tcx)),
+                goal.depth() + 1,
+                this,
+            )
+        })
+    }
+
+    /// If we have no candidates, then it's likely that there is a
+    /// non-well-formed alias in the goal.
+    fn detect_error_from_empty_candidates(
+        &mut self,
+        goal: &inspect::InspectGoal<'_, 'tcx>,
+    ) -> ControlFlow<PredicateObligation<'tcx>> {
+        let tcx = goal.infcx().tcx;
+        let pred_kind = goal.goal().predicate.kind();
+
+        match pred_kind.no_bound_vars() {
+            Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))) => {
+                self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?;
+            }
+            Some(ty::PredicateKind::NormalizesTo(pred))
+                if let ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst =
+                    pred.alias.kind(tcx) =>
+            {
+                self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?;
+                self.detect_non_well_formed_assoc_item(goal, pred.alias)?;
+            }
+            Some(_) | None => {}
+        }
+
+        ControlFlow::Break(self.obligation.clone())
+    }
 }
 
 impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
@@ -277,11 +354,19 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
 
     #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
     fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
-        let candidates = self.non_trivial_candidates(goal);
-        trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::<Vec<_>>());
+        let tcx = goal.infcx().tcx;
+        // Skip goals that aren't the *reason* for our goal's failure.
+        match (self.consider_ambiguities, goal.result()) {
+            (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
+            _ => return ControlFlow::Continue(()),
+        }
+        let pred_kind = goal.goal().predicate.kind();
 
-        let [candidate] = candidates.as_slice() else {
-            return ControlFlow::Break(self.obligation.clone());
+        let candidates = self.non_trivial_candidates(goal);
+        let candidate = match candidates.as_slice() {
+            [candidate] => candidate,
+            [] => return self.detect_error_from_empty_candidates(goal),
+            _ => return ControlFlow::Break(self.obligation.clone()),
         };
 
         // Don't walk into impls that have `do_not_recommend`.
@@ -291,13 +376,12 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
         } = candidate.kind()
             && goal.infcx().tcx.do_not_recommend_impl(impl_def_id)
         {
+            trace!("#[do_not_recommend] -> exit");
             return ControlFlow::Break(self.obligation.clone());
         }
 
-        let tcx = goal.infcx().tcx;
         // FIXME: Also, what about considering >1 layer up the stack? May be necessary
         // for normalizes-to.
-        let pred_kind = goal.goal().predicate.kind();
         let child_mode = match pred_kind.skip_binder() {
             ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
                 ChildMode::Trait(pred_kind.rebind(pred))
@@ -390,12 +474,6 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
                 }
             }
 
-            // Skip nested goals that aren't the *reason* for our goal's failure.
-            match (self.consider_ambiguities, nested_goal.result()) {
-                (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
-                _ => continue,
-            }
-
             self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
         }
 
diff --git a/tests/crashes/134905.rs b/tests/crashes/134905.rs
deleted file mode 100644
index 9f0f0f4b3f2..00000000000
--- a/tests/crashes/134905.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-//@ known-bug: #134905
-
-trait Iterate<'a> {
-    type Ty: Valid;
-}
-impl<'a, T> Iterate<'a> for T
-where
-    T: Check,
-{
-    default type Ty = ();
-}
-
-trait Check {}
-impl<'a, T> Eq for T where <T as Iterate<'a>>::Ty: Valid {}
-
-trait Valid {}
diff --git a/tests/ui/auto-traits/assoc-ty.next.stderr b/tests/ui/auto-traits/assoc-ty.next.stderr
index b9f56d6c99c..4ce00d17475 100644
--- a/tests/ui/auto-traits/assoc-ty.next.stderr
+++ b/tests/ui/auto-traits/assoc-ty.next.stderr
@@ -21,20 +21,21 @@ LL | | }
    = help: add `#![feature(auto_traits)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error[E0308]: mismatched types
-  --> $DIR/assoc-ty.rs:15:36
+error[E0271]: type mismatch resolving `<() as Trait>::Output normalizes-to _`
+  --> $DIR/assoc-ty.rs:15:12
    |
 LL |     let _: <() as Trait>::Output = ();
-   |            ---------------------   ^^ types differ
-   |            |
-   |            expected due to this
+   |            ^^^^^^^^^^^^^^^^^^^^^ types differ
+
+error[E0271]: type mismatch resolving `<() as Trait>::Output normalizes-to _`
+  --> $DIR/assoc-ty.rs:15:12
+   |
+LL |     let _: <() as Trait>::Output = ();
+   |            ^^^^^^^^^^^^^^^^^^^^^ types differ
    |
-   = note: expected associated type `<() as Trait>::Output`
-                    found unit type `()`
-   = help: consider constraining the associated type `<() as Trait>::Output` to `()` or calling a method that returns `<() as Trait>::Output`
-   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
 
-Some errors have detailed explanations: E0308, E0380, E0658.
-For more information about an error, try `rustc --explain E0308`.
+Some errors have detailed explanations: E0271, E0380, E0658.
+For more information about an error, try `rustc --explain E0271`.
diff --git a/tests/ui/auto-traits/assoc-ty.rs b/tests/ui/auto-traits/assoc-ty.rs
index ada75147f6e..efbfead9cd0 100644
--- a/tests/ui/auto-traits/assoc-ty.rs
+++ b/tests/ui/auto-traits/assoc-ty.rs
@@ -13,5 +13,7 @@ auto trait Trait {
 
 fn main() {
     let _: <() as Trait>::Output = ();
-    //~^ ERROR mismatched types
+    //[current]~^ ERROR mismatched types
+    //[next]~^^ ERROR type mismatch resolving `<() as Trait>::Output normalizes-to _`
+    //[next]~| ERROR type mismatch resolving `<() as Trait>::Output normalizes-to _`
 }
diff --git a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.next.stderr b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.next.stderr
index 3c24eb9adbe..ce64a022214 100644
--- a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.next.stderr
+++ b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.next.stderr
@@ -23,7 +23,19 @@ error[E0271]: type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> == ()`
 LL |     fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {}
    |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ
 
-error: aborting due to 3 previous errors
+error[E0271]: type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> normalizes-to _`
+  --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:28:73
+   |
+LL |     fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {}
+   |                                                                         ^^ types differ
+
+error[E0271]: type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> normalizes-to _`
+  --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:28:5
+   |
+LL |     fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ
+
+error: aborting due to 5 previous errors
 
 Some errors have detailed explanations: E0049, E0271, E0407.
 For more information about an error, try `rustc --explain E0049`.
diff --git a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs
index c97bd179943..cb32723b22d 100644
--- a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs
+++ b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs
@@ -26,9 +26,11 @@ impl X for Y {
     //~^ ERROR type `LineStream` has 0 type parameters but its trait declaration has 1 type parameter
     type LineStreamFut<'a, Repr> = impl Future<Output = Self::LineStream<'a, Repr>>;
     fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {}
-    //[current]~^ ERROR `()` is not a future
-    //[next]~^^ ERROR type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> == ()`
-    //~^^^ method `line_stream` is not a member of trait `X`
+    //~^ method `line_stream` is not a member of trait `X`
+    //[current]~^^ ERROR `()` is not a future
+    //[next]~^^^ ERROR type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> == ()`
+    //[next]~| ERROR type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> normalizes-to _`
+    //[next]~| ERROR type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> normalizes-to _`
 }
 
 pub fn main() {}
diff --git a/tests/ui/specialization/fuzzed/fuzzing-ice-134905.rs b/tests/ui/specialization/fuzzed/fuzzing-ice-134905.rs
new file mode 100644
index 00000000000..559c2345527
--- /dev/null
+++ b/tests/ui/specialization/fuzzed/fuzzing-ice-134905.rs
@@ -0,0 +1,22 @@
+// This test previously tried to use a tainted `EvalCtxt` when emitting
+// an error during coherence.
+#![feature(specialization)]
+//~^ WARN the feature `specialization` is incomplete
+trait Iterate<'a> {
+    type Ty: Valid;
+}
+impl<'a, T> Iterate<'a> for T
+where
+    T: Check,
+{
+    default type Ty = ();
+    //~^ ERROR the trait bound `(): Valid` is not satisfied
+}
+
+trait Check {}
+impl<'a, T> Eq for T where <T as Iterate<'a>>::Ty: Valid {}
+//~^ ERROR type parameter `T` must be used as the type parameter for some local type
+
+trait Valid {}
+
+fn main() {}
diff --git a/tests/ui/specialization/fuzzed/fuzzing-ice-134905.stderr b/tests/ui/specialization/fuzzed/fuzzing-ice-134905.stderr
new file mode 100644
index 00000000000..611fef1df66
--- /dev/null
+++ b/tests/ui/specialization/fuzzed/fuzzing-ice-134905.stderr
@@ -0,0 +1,40 @@
+warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/fuzzing-ice-134905.rs:3:12
+   |
+LL | #![feature(specialization)]
+   |            ^^^^^^^^^^^^^^
+   |
+   = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
+   = help: consider using `min_specialization` instead, which is more stable and complete
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0277]: the trait bound `(): Valid` is not satisfied
+  --> $DIR/fuzzing-ice-134905.rs:12:23
+   |
+LL |     default type Ty = ();
+   |                       ^^ the trait `Valid` is not implemented for `()`
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/fuzzing-ice-134905.rs:20:1
+   |
+LL | trait Valid {}
+   | ^^^^^^^^^^^
+note: required by a bound in `Iterate::Ty`
+  --> $DIR/fuzzing-ice-134905.rs:6:14
+   |
+LL |     type Ty: Valid;
+   |              ^^^^^ required by this bound in `Iterate::Ty`
+
+error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
+  --> $DIR/fuzzing-ice-134905.rs:17:10
+   |
+LL | impl<'a, T> Eq for T where <T as Iterate<'a>>::Ty: Valid {}
+   |          ^ type parameter `T` must be used as the type parameter for some local type
+   |
+   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
+   = note: only traits defined in the current crate can be implemented for a type parameter
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
+Some errors have detailed explanations: E0210, E0277.
+For more information about an error, try `rustc --explain E0210`.