about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2025-01-23 19:54:25 +0100
committerGitHub <noreply@github.com>2025-01-23 19:54:25 +0100
commitdafc861aa31831887fb0e634b07711e4f6ed6e66 (patch)
treecf57932190995f8556962062d838849ca437fa49
parent4496f23ca9f7dcc9e85209a3a99f6e911b1009f5 (diff)
parent09b784fac560d725ffc37b6fad1037615f63b232 (diff)
Rollup merge of #135766 - lcnr:candidate-assembly-3, r=compiler-errors
handle global trait bounds defining assoc types

This also fixes the compare-mode for
- tests/ui/coherence/coherent-due-to-fulfill.rs
- tests/ui/codegen/mono-impossible-2.rs
- tests/ui/trivial-bounds/trivial-bounds-inconsistent-projection.rs
- tests/ui/nll/issue-61320-normalize.rs

I first considered the alternative to always prefer where-bounds during normalization, regardless of how the trait goal has been proven by changing `fn merge_candidates` instead. https://github.com/rust-lang/rust/blob/ecda83b30f0f68cf5692855dddc0bc38ee8863fc/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs#L785

This approach is more restrictive than behavior of the old solver to avoid mismatches between trait and normalization goals. This may be breaking in case the where-bound adds unnecessary region constraints and we currently don't ever try to normalize an associated type. I would like to detect these cases and change the approach to exactly match the old solver if required. I want to minimize cases where attempting to normalize in more places causes code to break.

r? `@compiler-errors`
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_next_trait_solver/Cargo.toml1
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs48
-rw-r--r--tests/ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs21
4 files changed, 49 insertions, 22 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f03b033db6f..10889139e8d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4260,7 +4260,6 @@ dependencies = [
  "rustc_serialize",
  "rustc_type_ir",
  "rustc_type_ir_macros",
- "smallvec",
  "tracing",
 ]
 
diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml
index f9168112216..451c215566b 100644
--- a/compiler/rustc_next_trait_solver/Cargo.toml
+++ b/compiler/rustc_next_trait_solver/Cargo.toml
@@ -13,7 +13,6 @@ rustc_macros = { path = "../rustc_macros", optional = true }
 rustc_serialize = { path = "../rustc_serialize", optional = true }
 rustc_type_ir = { path = "../rustc_type_ir", default-features = false }
 rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
-smallvec = "1.8.1"
 tracing = "0.1"
 # tidy-alphabetical-end
 
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index 4faa243c02a..513fc9355c8 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -8,7 +8,6 @@ use rustc_type_ir::lang_items::TraitSolverLangItem;
 use rustc_type_ir::solve::CanonicalResponse;
 use rustc_type_ir::visit::TypeVisitableExt as _;
 use rustc_type_ir::{self as ty, Interner, TraitPredicate, TypingMode, Upcast as _, elaborate};
-use smallvec::SmallVec;
 use tracing::{instrument, trace};
 
 use crate::delegate::SolverDelegate;
@@ -1199,33 +1198,42 @@ where
         // nested requirements, over all others. This is a fix for #53123 and
         // prevents where-bounds from accidentally extending the lifetime of a
         // variable.
-        if candidates
-            .iter()
-            .any(|c| matches!(c.source, CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial)))
-        {
-            let trivial_builtin_impls: SmallVec<[_; 1]> = candidates
-                .iter()
-                .filter(|c| {
-                    matches!(c.source, CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial))
-                })
-                .map(|c| c.result)
-                .collect();
+        let mut trivial_builtin_impls = candidates.iter().filter(|c| {
+            matches!(c.source, CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial))
+        });
+        if let Some(candidate) = trivial_builtin_impls.next() {
             // There should only ever be a single trivial builtin candidate
             // as they would otherwise overlap.
-            assert_eq!(trivial_builtin_impls.len(), 1);
-            return if let Some(response) = self.try_merge_responses(&trivial_builtin_impls) {
-                Ok((response, Some(TraitGoalProvenVia::Misc)))
-            } else {
-                Ok((self.bail_with_ambiguity(&trivial_builtin_impls), None))
-            };
+            assert!(trivial_builtin_impls.next().is_none());
+            return Ok((candidate.result, Some(TraitGoalProvenVia::Misc)));
         }
 
         // If there are non-global where-bounds, prefer where-bounds
         // (including global ones) over everything else.
         let has_non_global_where_bounds = candidates.iter().any(|c| match c.source {
             CandidateSource::ParamEnv(idx) => {
-                let where_bound = goal.param_env.caller_bounds().get(idx);
-                where_bound.has_bound_vars() || !where_bound.is_global()
+                let where_bound = goal.param_env.caller_bounds().get(idx).unwrap();
+                let ty::ClauseKind::Trait(trait_pred) = where_bound.kind().skip_binder() else {
+                    unreachable!("expected trait-bound: {where_bound:?}");
+                };
+
+                if trait_pred.has_bound_vars() || !trait_pred.is_global() {
+                    return true;
+                }
+
+                // We don't consider a trait-bound global if it has a projection bound.
+                //
+                // See ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs
+                // for an example where this is necessary.
+                for p in goal.param_env.caller_bounds().iter() {
+                    if let ty::ClauseKind::Projection(proj) = p.kind().skip_binder() {
+                        if proj.projection_term.trait_ref(self.cx()) == trait_pred.trait_ref {
+                            return true;
+                        }
+                    }
+                }
+
+                false
             }
             _ => false,
         });
diff --git a/tests/ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs b/tests/ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs
new file mode 100644
index 00000000000..dc96652f82f
--- /dev/null
+++ b/tests/ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs
@@ -0,0 +1,21 @@
+//@ compile-flags: -Znext-solver
+//@ check-pass
+
+// `(): Trait` is a global where-bound with a projection bound.
+// This previously resulted in ambiguity as we considered both
+// the impl and the where-bound while normalizing.
+
+trait Trait {
+    type Assoc;
+}
+impl Trait for () {
+    type Assoc = &'static ();
+}
+
+fn foo<'a>(x: <() as Trait>::Assoc)
+where
+    (): Trait<Assoc = &'a ()>,
+{
+}
+
+fn main() {}