about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-05-01 18:06:53 +0000
committerbors <bors@rust-lang.org>2024-05-01 18:06:53 +0000
commitbe9bca2daf929c4472abd8fbbb94b00f82481f07 (patch)
treef5c4be1935b2986cfc19b56ab548a334fecabcbf
parent9ba3d315b4b2bea7485071eaf4b2681320cd2a27 (diff)
parent0dbe07f201a5e5d7ac5e6936dcc72cca9d6c26b0 (diff)
downloadrust-be9bca2daf929c4472abd8fbbb94b00f82481f07.tar.gz
rust-be9bca2daf929c4472abd8fbbb94b00f82481f07.zip
Auto merge of #124590 - matthiaskrgr:rollup-091hpnp, r=matthiaskrgr
Rollup of 2 pull requests

Successful merges:

 - #124542 (Add diagnostic item for `std::iter::Iterator::enumerate`)
 - #124566 (fix `NormalizesTo` proof tree issue)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs170
-rw-r--r--library/core/src/iter/traits/iterator.rs1
-rw-r--r--tests/ui/specialization/specialization-overlap-projection.current.stderr (renamed from tests/ui/specialization/specialization-overlap-projection.stderr)2
-rw-r--r--tests/ui/specialization/specialization-overlap-projection.next.stderr49
-rw-r--r--tests/ui/specialization/specialization-overlap-projection.rs10
6 files changed, 181 insertions, 52 deletions
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 8d38867ab1f..7fe94c2e82b 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -754,6 +754,7 @@ symbols! {
         enable,
         encode,
         end,
+        enumerate_method,
         env,
         env_CFG_RELEASE: env!("CFG_RELEASE"),
         eprint_macro,
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index 1c13446213b..7c6dd4d3feb 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -14,6 +14,7 @@ use rustc_ast_ir::visit::VisitorResult;
 use rustc_infer::infer::resolve::EagerResolver;
 use rustc_infer::infer::type_variable::TypeVariableOrigin;
 use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
+use rustc_infer::traits::{TraitEngine, TraitEngineExt};
 use rustc_macros::extension;
 use rustc_middle::infer::unify_key::ConstVariableOrigin;
 use rustc_middle::traits::query::NoSolution;
@@ -22,9 +23,10 @@ use rustc_middle::traits::solve::{Certainty, Goal};
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty;
 use rustc_middle::ty::TypeFoldable;
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
 
 use crate::solve::eval_ctxt::canonical;
+use crate::solve::FulfillmentCtxt;
 use crate::solve::{EvalCtxt, GoalEvaluationKind, GoalSource};
 use crate::solve::{GenerateProofTree, InferCtxtEvalExt};
 
@@ -37,7 +39,54 @@ pub struct InspectGoal<'a, 'tcx> {
     depth: usize,
     orig_values: Vec<ty::GenericArg<'tcx>>,
     goal: Goal<'tcx, ty::Predicate<'tcx>>,
-    evaluation: inspect::CanonicalGoalEvaluation<'tcx>,
+    result: Result<Certainty, NoSolution>,
+    evaluation_kind: inspect::CanonicalGoalEvaluationKind<'tcx>,
+    normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>,
+}
+
+/// The expected term of a `NormalizesTo` goal gets replaced
+/// with an unconstrained inference variable when computing
+/// `NormalizesTo` goals and we return the nested goals to the
+/// caller, who also equates the actual term with the expected.
+///
+/// This is an implementation detail of the trait solver and
+/// not something we want to leak to users. We therefore
+/// treat `NormalizesTo` goals as if they apply the expected
+/// type at the end of each candidate.
+#[derive(Copy, Clone)]
+struct NormalizesToTermHack<'tcx> {
+    term: ty::Term<'tcx>,
+    unconstrained_term: ty::Term<'tcx>,
+}
+
+impl<'tcx> NormalizesToTermHack<'tcx> {
+    /// Relate the `term` with the new `unconstrained_term` created
+    /// when computing the proof tree for this `NormalizesTo` goals.
+    /// This handles nested obligations.
+    fn constrain(
+        self,
+        infcx: &InferCtxt<'tcx>,
+        span: Span,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Result<Certainty, NoSolution> {
+        infcx
+            .at(&ObligationCause::dummy_with_span(span), param_env)
+            .eq(DefineOpaqueTypes::Yes, self.term, self.unconstrained_term)
+            .map_err(|_| NoSolution)
+            .and_then(|InferOk { value: (), obligations }| {
+                let mut fulfill_cx = FulfillmentCtxt::new(infcx);
+                fulfill_cx.register_predicate_obligations(infcx, obligations);
+                if fulfill_cx.select_where_possible(infcx).is_empty() {
+                    if fulfill_cx.pending_obligations().is_empty() {
+                        Ok(Certainty::Yes)
+                    } else {
+                        Ok(Certainty::AMBIGUOUS)
+                    }
+                } else {
+                    Err(NoSolution)
+                }
+            })
+    }
 }
 
 pub struct InspectCandidate<'a, 'tcx> {
@@ -115,42 +164,47 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
             self.final_state,
         );
 
+        if let Some(term_hack) = self.goal.normalizes_to_term_hack {
+            // FIXME: We ignore the expected term of `NormalizesTo` goals
+            // when computing the result of its candidates. This is
+            // scuffed.
+            let _ = term_hack.constrain(infcx, span, param_env);
+        }
+
         instantiated_goals
             .into_iter()
-            .map(|goal| {
-                let proof_tree = match goal.predicate.kind().no_bound_vars() {
-                    Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
-                        let unconstrained_term = match term.unpack() {
-                            ty::TermKind::Ty(_) => infcx
-                                .next_ty_var(TypeVariableOrigin { param_def_id: None, span })
-                                .into(),
-                            ty::TermKind::Const(ct) => infcx
-                                .next_const_var(
-                                    ct.ty(),
-                                    ConstVariableOrigin { param_def_id: None, span },
-                                )
-                                .into(),
-                        };
-                        let goal = goal
-                            .with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
-                        let proof_tree =
-                            EvalCtxt::enter_root(infcx, GenerateProofTree::Yes, |ecx| {
-                                ecx.evaluate_goal_raw(
-                                    GoalEvaluationKind::Root,
-                                    GoalSource::Misc,
-                                    goal,
-                                )
-                            })
-                            .1;
-                        let InferOk { value: (), obligations: _ } = infcx
-                            .at(&ObligationCause::dummy_with_span(span), param_env)
-                            .eq(DefineOpaqueTypes::Yes, term, unconstrained_term)
-                            .unwrap();
-                        proof_tree
-                    }
-                    _ => infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1,
-                };
-                InspectGoal::new(infcx, self.goal.depth + 1, proof_tree.unwrap())
+            .map(|goal| match goal.predicate.kind().no_bound_vars() {
+                Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
+                    let unconstrained_term = match term.unpack() {
+                        ty::TermKind::Ty(_) => infcx
+                            .next_ty_var(TypeVariableOrigin { param_def_id: None, span })
+                            .into(),
+                        ty::TermKind::Const(ct) => infcx
+                            .next_const_var(
+                                ct.ty(),
+                                ConstVariableOrigin { param_def_id: None, span },
+                            )
+                            .into(),
+                    };
+                    let goal =
+                        goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
+                    let proof_tree = EvalCtxt::enter_root(infcx, GenerateProofTree::Yes, |ecx| {
+                        ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal)
+                    })
+                    .1;
+                    InspectGoal::new(
+                        infcx,
+                        self.goal.depth + 1,
+                        proof_tree.unwrap(),
+                        Some(NormalizesToTermHack { term, unconstrained_term }),
+                    )
+                }
+                _ => InspectGoal::new(
+                    infcx,
+                    self.goal.depth + 1,
+                    infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1.unwrap(),
+                    None,
+                ),
             })
             .collect()
     }
@@ -172,7 +226,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
     }
 
     pub fn result(&self) -> Result<Certainty, NoSolution> {
-        self.evaluation.result.map(|c| c.value.certainty)
+        self.result
     }
 
     fn candidates_recur(
@@ -229,11 +283,11 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
 
     pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> {
         let mut candidates = vec![];
-        let last_eval_step = match self.evaluation.kind {
+        let last_eval_step = match self.evaluation_kind {
             inspect::CanonicalGoalEvaluationKind::Overflow
             | inspect::CanonicalGoalEvaluationKind::CycleInStack
             | inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit => {
-                warn!("unexpected root evaluation: {:?}", self.evaluation);
+                warn!("unexpected root evaluation: {:?}", self.evaluation_kind);
                 return vec![];
             }
             inspect::CanonicalGoalEvaluationKind::Evaluation { revisions } => {
@@ -262,17 +316,33 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
         candidates.pop().filter(|_| candidates.is_empty())
     }
 
-    fn new(infcx: &'a InferCtxt<'tcx>, depth: usize, root: inspect::GoalEvaluation<'tcx>) -> Self {
+    fn new(
+        infcx: &'a InferCtxt<'tcx>,
+        depth: usize,
+        root: inspect::GoalEvaluation<'tcx>,
+        normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>,
+    ) -> Self {
         let inspect::GoalEvaluation { uncanonicalized_goal, kind, evaluation } = root;
-        match kind {
-            inspect::GoalEvaluationKind::Root { orig_values } => InspectGoal {
-                infcx,
-                depth,
-                orig_values,
-                goal: uncanonicalized_goal.fold_with(&mut EagerResolver::new(infcx)),
-                evaluation,
-            },
-            inspect::GoalEvaluationKind::Nested { .. } => unreachable!(),
+        let inspect::GoalEvaluationKind::Root { orig_values } = kind else { unreachable!() };
+
+        let result = evaluation.result.and_then(|ok| {
+            if let Some(term_hack) = normalizes_to_term_hack {
+                infcx
+                    .probe(|_| term_hack.constrain(infcx, DUMMY_SP, uncanonicalized_goal.param_env))
+                    .map(|certainty| ok.value.certainty.unify_with(certainty))
+            } else {
+                Ok(ok.value.certainty)
+            }
+        });
+
+        InspectGoal {
+            infcx,
+            depth,
+            orig_values,
+            goal: uncanonicalized_goal.fold_with(&mut EagerResolver::new(infcx)),
+            result,
+            evaluation_kind: evaluation.kind,
+            normalizes_to_term_hack,
         }
     }
 }
@@ -299,6 +369,6 @@ impl<'tcx> InferCtxt<'tcx> {
     ) -> V::Result {
         let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
         let proof_tree = proof_tree.unwrap();
-        visitor.visit_goal(&InspectGoal::new(self, 0, proof_tree))
+        visitor.visit_goal(&InspectGoal::new(self, 0, proof_tree, None))
     }
 }
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index 7c1c6122efe..cee99e28b5a 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -974,6 +974,7 @@ pub trait Iterator {
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_do_not_const_check]
+    #[cfg_attr(not(test), rustc_diagnostic_item = "enumerate_method")]
     fn enumerate(self) -> Enumerate<Self>
     where
         Self: Sized,
diff --git a/tests/ui/specialization/specialization-overlap-projection.stderr b/tests/ui/specialization/specialization-overlap-projection.current.stderr
index 708c0817fd9..a69826fa96b 100644
--- a/tests/ui/specialization/specialization-overlap-projection.stderr
+++ b/tests/ui/specialization/specialization-overlap-projection.current.stderr
@@ -1,5 +1,5 @@
 warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/specialization-overlap-projection.rs:7:12
+  --> $DIR/specialization-overlap-projection.rs:10:12
    |
 LL | #![feature(specialization)]
    |            ^^^^^^^^^^^^^^
diff --git a/tests/ui/specialization/specialization-overlap-projection.next.stderr b/tests/ui/specialization/specialization-overlap-projection.next.stderr
new file mode 100644
index 00000000000..ab040193fa4
--- /dev/null
+++ b/tests/ui/specialization/specialization-overlap-projection.next.stderr
@@ -0,0 +1,49 @@
+warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/specialization-overlap-projection.rs:10: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[E0119]: conflicting implementations of trait `Foo` for type `u32`
+  --> $DIR/specialization-overlap-projection.rs:28:1
+   |
+LL | impl Foo for u32 {}
+   | ---------------- first implementation here
+LL | impl Foo for <u8 as Assoc>::Output {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32`
+
+error[E0119]: conflicting implementations of trait `Foo` for type `u32`
+  --> $DIR/specialization-overlap-projection.rs:30:1
+   |
+LL | impl Foo for u32 {}
+   | ---------------- first implementation here
+...
+LL | impl Foo for <u16 as Assoc>::Output {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32`
+
+error[E0282]: type annotations needed
+  --> $DIR/specialization-overlap-projection.rs:17:27
+   |
+LL |     default type Output = bool;
+   |                           ^^^^ cannot infer type for associated type `<T as Assoc>::Output`
+
+error[E0282]: type annotations needed
+  --> $DIR/specialization-overlap-projection.rs:21:35
+   |
+LL | impl Assoc for u8 { type Output = u8; }
+   |                                   ^^ cannot infer type for associated type `<u8 as Assoc>::Output`
+
+error[E0282]: type annotations needed
+  --> $DIR/specialization-overlap-projection.rs:23:36
+   |
+LL | impl Assoc for u16 { type Output = u16; }
+   |                                    ^^^ cannot infer type for associated type `<u16 as Assoc>::Output`
+
+error: aborting due to 5 previous errors; 1 warning emitted
+
+Some errors have detailed explanations: E0119, E0282.
+For more information about an error, try `rustc --explain E0119`.
diff --git a/tests/ui/specialization/specialization-overlap-projection.rs b/tests/ui/specialization/specialization-overlap-projection.rs
index 66951b9d50c..78e75f623c4 100644
--- a/tests/ui/specialization/specialization-overlap-projection.rs
+++ b/tests/ui/specialization/specialization-overlap-projection.rs
@@ -1,4 +1,7 @@
-//@ check-pass
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+//@[current] check-pass
 
 // Test that impls on projected self types can resolve overlap, even when the
 // projections involve specialization, so long as the associated type is
@@ -12,14 +15,19 @@ trait Assoc {
 
 impl<T> Assoc for T {
     default type Output = bool;
+    //[next]~^ ERROR type annotations needed
 }
 
 impl Assoc for u8 { type Output = u8; }
+//[next]~^ ERROR type annotations needed
 impl Assoc for u16 { type Output = u16; }
+//[next]~^ ERROR type annotations needed
 
 trait Foo {}
 impl Foo for u32 {}
 impl Foo for <u8 as Assoc>::Output {}
+//[next]~^ ERROR conflicting implementations of trait `Foo` for type `u32`
 impl Foo for <u16 as Assoc>::Output {}
+//[next]~^ ERROR conflicting implementations of trait `Foo` for type `u32`
 
 fn main() {}