From a913c243daf3e716f8e060adde046d7f98813a83 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 9 Feb 2024 10:06:16 +0100 Subject: [PATCH 1/2] add comment --- compiler/rustc_trait_selection/src/solve/assembly/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 239072dfc8e0e..21a2ba02cd7fa 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -779,6 +779,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } + /// In coherence we have to not only care about all impls we know about, but + /// also consider impls which may get added in a downstream or sibling crate + /// or which an upstream impl may add in a minor release. + /// + /// To do so we add an ambiguous candidate in case such an unknown impl could + /// apply to the current goal. #[instrument(level = "debug", skip_all)] fn assemble_coherence_unknowable_candidates>( &mut self, From 50516379791cc8f6b51971d01c7dec68c9ba1f7a Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 9 Feb 2024 10:40:26 +0100 Subject: [PATCH 2/2] hide impls if trait bound is proven from env --- .../src/solve/assembly/mod.rs | 112 +++++++++++------- .../cycles/fixpoint-rerun-all-cycle-heads.rs | 12 ++ .../fixpoint-rerun-all-cycle-heads.stderr | 2 +- .../env-shadows-impls/ambig-env-no-shadow.rs | 40 +++++++ .../discard-impls-shadowed-by-env-1.rs | 30 +++++ .../discard-impls-shadowed-by-env-2.rs | 29 +++++ .../discard-impls-shadowed-by-env-3.rs | 18 +++ ...zes_to_ignores_unnormalizable_candidate.rs | 29 +++++ ...o_ignores_unnormalizable_candidate.stderr} | 10 +- .../param-candidate-shadows-project.rs | 31 +++++ .../param-candidate-shadows-project.stderr | 19 +++ .../occurs-check-nested-alias.next.stderr | 8 +- .../next-solver/normalize-param-env-2.rs | 17 ++- .../next-solver/normalize-param-env-2.stderr | 35 ++++++ .../normalize-param-env-4.next.stderr | 19 +++ .../next-solver/normalize-param-env-4.rs | 34 ++++++ ...zes_to_ignores_unnormalizable_candidate.rs | 40 ------- .../param-candidate-doesnt-shadow-project.rs | 25 ---- 18 files changed, 382 insertions(+), 128 deletions(-) create mode 100644 tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs create mode 100644 tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-1.rs create mode 100644 tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-2.rs create mode 100644 tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-3.rs create mode 100644 tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.rs rename tests/ui/traits/next-solver/{normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr => env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.stderr} (70%) create mode 100644 tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.rs create mode 100644 tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr create mode 100644 tests/ui/traits/next-solver/normalize-param-env-2.stderr create mode 100644 tests/ui/traits/next-solver/normalize-param-env-4.next.stderr create mode 100644 tests/ui/traits/next-solver/normalize-param-env-4.rs delete mode 100644 tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.rs delete mode 100644 tests/ui/traits/next-solver/param-candidate-doesnt-shadow-project.rs diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 21a2ba02cd7fa..3b9515e16701a 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -8,7 +8,7 @@ use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::Reveal; use rustc_middle::traits::solve::inspect::ProbeKind; use rustc_middle::traits::solve::{ - CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult, + CandidateSource, CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult, }; use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams}; @@ -276,25 +276,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, goal: Goal<'tcx, G>, ) -> Vec> { - let dummy_candidate = |this: &mut EvalCtxt<'_, 'tcx>, certainty| { - let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); - let result = this.evaluate_added_goals_and_make_canonical_response(certainty).unwrap(); - let mut dummy_probe = this.inspect.new_probe(); - dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) }); - this.inspect.finish_probe(dummy_probe); - vec![Candidate { source, result }] - }; - let Some(normalized_self_ty) = self.try_normalize_ty(goal.param_env, goal.predicate.self_ty()) else { debug!("overflow while evaluating self type"); - return dummy_candidate(self, Certainty::OVERFLOW); + return self.forced_ambiguity(MaybeCause::Overflow); }; if normalized_self_ty.is_ty_var() { debug!("self type has been normalized to infer"); - return dummy_candidate(self, Certainty::AMBIGUOUS); + return self.forced_ambiguity(MaybeCause::Ambiguity); } let goal = @@ -315,11 +306,26 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.assemble_param_env_candidates(goal, &mut candidates); - self.assemble_coherence_unknowable_candidates(goal, &mut candidates); + match self.solver_mode() { + SolverMode::Normal => self.discard_impls_shadowed_by_env(goal, &mut candidates), + SolverMode::Coherence => { + self.assemble_coherence_unknowable_candidates(goal, &mut candidates) + } + } candidates } + fn forced_ambiguity(&mut self, cause: MaybeCause) -> Vec> { + let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); + let certainty = Certainty::Maybe(cause); + let result = self.evaluate_added_goals_and_make_canonical_response(certainty).unwrap(); + let mut dummy_probe = self.inspect.new_probe(); + dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) }); + self.inspect.finish_probe(dummy_probe); + vec![Candidate { source, result }] + } + #[instrument(level = "debug", skip_all)] fn assemble_non_blanket_impl_candidates>( &mut self, @@ -792,11 +798,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates: &mut Vec>, ) { let tcx = self.tcx(); - match self.solver_mode() { - SolverMode::Normal => return, - SolverMode::Coherence => {} - }; - let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| { let trait_ref = goal.predicate.trait_ref(tcx); #[derive(Debug)] @@ -826,6 +827,51 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } + /// If there's a where-bound for the current goal, do not use any impl candidates + /// to prove the current goal. Most importantly, if there is a where-bound which does + /// not specify any associated types, we do not allow normalizing the associated type + /// by using an impl, even if it would apply. + /// + /// + // FIXME(@lcnr): The current structure here makes me unhappy and feels ugly. idk how + // to improve this however. However, this should make it fairly straightforward to refine + // the filtering going forward, so it seems alright-ish for now. + fn discard_impls_shadowed_by_env>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec>, + ) { + let tcx = self.tcx(); + let trait_goal: Goal<'tcx, ty::TraitPredicate<'tcx>> = + goal.with(tcx, goal.predicate.trait_ref(tcx)); + let mut trait_candidates_from_env = Vec::new(); + self.assemble_param_env_candidates(trait_goal, &mut trait_candidates_from_env); + self.assemble_alias_bound_candidates(trait_goal, &mut trait_candidates_from_env); + if !trait_candidates_from_env.is_empty() { + let trait_env_result = self.merge_candidates(trait_candidates_from_env); + match trait_env_result.unwrap().value.certainty { + // If proving the trait goal succeeds by using the env, + // we freely drop all impl candidates. + // + // FIXME(@lcnr): It feels like this could easily hide + // a forced ambiguity candidate added earlier. + // This feels dangerous. + Certainty::Yes => { + candidates.retain(|c| match c.source { + CandidateSource::Impl(_) | CandidateSource::BuiltinImpl(_) => false, + CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => true, + }); + } + // If it is still ambiguous we instead just force the whole goal + // to be ambig and wait for inference constraints. See + // tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs + Certainty::Maybe(cause) => { + *candidates = self.forced_ambiguity(cause); + } + } + } + } + /// If there are multiple ways to prove a trait or projection goal, we have /// to somehow try to merge the candidates into one. If that fails, we return /// ambiguity. @@ -838,34 +884,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let responses = candidates.iter().map(|c| c.result).collect::>(); if let Some(result) = self.try_merge_responses(&responses) { return Ok(result); + } else { + self.flounder(&responses) } - - // We then check whether we should prioritize `ParamEnv` candidates. - // - // Doing so is incomplete and would therefore be unsound during coherence. - match self.solver_mode() { - SolverMode::Coherence => (), - // Prioritize `ParamEnv` candidates only if they do not guide inference. - // - // This is still incomplete as we may add incorrect region bounds. - SolverMode::Normal => { - let param_env_responses = candidates - .iter() - .filter(|c| { - matches!( - c.source, - CandidateSource::ParamEnv(_) | CandidateSource::AliasBound - ) - }) - .map(|c| c.result) - .collect::>(); - if let Some(result) = self.try_merge_responses(¶m_env_responses) { - // We strongly prefer alias and param-env bounds here, even if they affect inference. - // See https://github.com/rust-lang/trait-system-refactor-initiative/issues/11. - return Ok(result); - } - } - } - self.flounder(&responses) } } diff --git a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs index c7e2e2d5e040b..f6c75317a34e4 100644 --- a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs +++ b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs @@ -24,6 +24,18 @@ where { } +// HACK: This impls is necessary so that the impl above is well-formed. +// +// When checking that the impl above is well-formed we check `B: Trait<'a, 'b>` +// with the where clauses `A: Trait<'a, 'b>` and `A NotImplemented`. Trying to +// use the impl itself to prove that adds region constraints as we uniquified the +// regions in the `A: Trait<'a, 'b>` where-bound. As both the impl above +// and the impl below now apply with some constraints, we failed with ambiguity. +impl<'a, 'b, T: ?Sized> Trait<'a, 'b> for B +where + A: NotImplemented, +{} + // This impl directly requires 'b to be equal to 'static. // // Because of the coinductive cycle through `C` it also requires diff --git a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr index 7b3075f4ff35a..0cbd96540448c 100644 --- a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr +++ b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/fixpoint-rerun-all-cycle-heads.rs:47:5 + --> $DIR/fixpoint-rerun-all-cycle-heads.rs:59:5 | LL | fn check<'a, T: ?Sized>() { | -- lifetime `'a` defined here diff --git a/tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs b/tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs new file mode 100644 index 0000000000000..37730d38c7a64 --- /dev/null +++ b/tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs @@ -0,0 +1,40 @@ +// compile-flags: -Znext-solver +// check-pass + +// If a trait goal is proven using the environment, we discard +// impl candidates when normalizing. However, in this example +// the env candidates start as ambiguous and end up not applying, +// so normalization should succeed later on. + +trait Trait: Sized { + type Assoc: From; +} + +impl Trait for T { + type Assoc = T; +} + +fn mk_assoc, U>(t: T, _: U) -> >::Assoc { + t.into() +} + +fn generic(t: T) -> T +where + T: Trait, + T: Trait, +{ + let u = Default::default(); + + // at this point we have 2 ambig env candidates + let ret: T = mk_assoc(t, u); + + // now both env candidates don't apply, so we're now able to + // normalize using this impl candidates. For this to work + // the normalizes-to must have remained ambiguous above. + let _: u8 = u; + ret +} + +fn main() { + assert_eq!(generic(1), 1); +} diff --git a/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-1.rs b/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-1.rs new file mode 100644 index 0000000000000..63742d0d1a1e8 --- /dev/null +++ b/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-1.rs @@ -0,0 +1,30 @@ +// compile-flags: -Znext-solver +// check-pass + +// Normalizing `::TraitAssoc` in the elaborated environment +// `[T: Trait, T: Super, ::SuperAssoc = ::TraitAssoc]` +// has a single impl candidate, which uses the environment to +// normalize `::TraitAssoc` to itself. We avoid this overflow +// by discarding impl candidates the trait bound is proven by a where-clause. + +// https://github.com/rust-lang/trait-system-refactor-initiative/issues/76 +trait Super { + type SuperAssoc; +} + +trait Trait: Super { + type TraitAssoc; +} + +impl Trait for T +where + T: Super, +{ + type TraitAssoc = U; +} + +fn overflow() { + let x: ::TraitAssoc; +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-2.rs b/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-2.rs new file mode 100644 index 0000000000000..b0ef0d44baf80 --- /dev/null +++ b/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-2.rs @@ -0,0 +1,29 @@ +// revisions: next current +//[next] compile-flags: -Znext-solver +// check-pass + +#![allow(warnings)] +trait Trait { + type Assoc; +} + +impl Trait for T { + type Assoc = T; +} + +fn lazy_init, U>() -> (T, >::Assoc) { + todo!() +} + +fn foo>(x: T) { + // When considering impl candidates to be equally valid as env candidates + // this ends up being ambiguous as `U` can be both `u32ยด and `u64` here. + // + // This is acceptable breakage but we should still note that it's + // theoretically breaking. + let (delayed, mut proj) = lazy_init::<_, _>(); + proj = x; + let _: T = delayed; +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-3.rs b/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-3.rs new file mode 100644 index 0000000000000..807e19a4a5869 --- /dev/null +++ b/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-3.rs @@ -0,0 +1,18 @@ +// compile-flags: -Znext-solver +// check-pass + +// If we normalize using the impl here the constraints from normalization and +// trait goals can differ. This is especially bad if normalization results +// in stronger constraints. +trait Trait<'a> { + type Assoc; +} + +impl Trait<'static> for T { + type Assoc = (); +} + +// normalizing requires `'a == 'static`, the trait bound does not. +fn foo<'a, T: Trait<'a>>(_: T::Assoc) {} + +fn main() {} diff --git a/tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.rs b/tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.rs new file mode 100644 index 0000000000000..af2c44ea23398 --- /dev/null +++ b/tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.rs @@ -0,0 +1,29 @@ +// compile-flags: -Znext-solver + +// Checks whether the new solver is smart enough to infer `?0 = U` when solving: +// `normalizes-to( as Trait>::Assoc, u8)` +// with `normalizes-to( as Trait>::Assoc, u8)` in the paramenv even when +// there is a separate `Vec: Trait` bound in the paramenv. +// +// We currently intentionally do not guide inference this way. + +trait Trait { + type Assoc; +} + +fn foo>(x: T) {} + +fn unconstrained() -> Vec { + todo!() +} + +fn bar() +where + Vec: Trait, + Vec: Trait, +{ + foo(unconstrained()) + //~^ ERROR type annotations needed +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr b/tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.stderr similarity index 70% rename from tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr rename to tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.stderr index c1a8b74df0824..36d281e11dd7a 100644 --- a/tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr +++ b/tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.stderr @@ -1,21 +1,21 @@ error[E0283]: type annotations needed - --> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:36:5 + --> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:25:5 | LL | foo(unconstrained()) | ^^^ --------------- type must be known at this point | | | cannot infer type of the type parameter `T` declared on the function `foo` | - = note: cannot satisfy `_: Trait` + = note: cannot satisfy `Vec<_>: Trait` note: required by a bound in `foo` - --> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:19:11 + --> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:14:11 | LL | fn foo>(x: T) {} | ^^^^^^^^^^^^^^^^^ required by this bound in `foo` help: consider specifying the generic argument | -LL | foo::(unconstrained()) - | +++++ +LL | foo::>(unconstrained()) + | ++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.rs b/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.rs new file mode 100644 index 0000000000000..5989e605bd9aa --- /dev/null +++ b/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.rs @@ -0,0 +1,31 @@ +// compile-flags: -Znext-solver + +trait Foo { + type Assoc; +} + +trait Bar {} + +impl Foo for T { + type Assoc = i32; +} + +impl Bar for T where T: Foo {} + +fn require_bar() {} + +fn foo() { + // Unlike the classic solver, the new solver previously projected + // `::Assoc = _` down to `i32` even though there's a param-env + // candidate here, since we don't assemble any param-env projection + // candidates for `T: Foo` alone. + // + // However, allowing impl candidates shadowed by env candidates results + // in multiple issues, so we explicitly hide them, e.g. + // + // https://github.com/rust-lang/trait-system-refactor-initiative/issues/76 + require_bar::(); + //~^ ERROR the trait bound `T: Bar` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr b/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr new file mode 100644 index 0000000000000..2785357e792de --- /dev/null +++ b/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `T: Bar` is not satisfied + --> $DIR/param-candidate-shadows-project.rs:27:19 + | +LL | require_bar::(); + | ^ the trait `Bar` is not implemented for `T` + | +note: required by a bound in `require_bar` + --> $DIR/param-candidate-shadows-project.rs:15:19 + | +LL | fn require_bar() {} + | ^^^ required by this bound in `require_bar` +help: consider further restricting this bound + | +LL | fn foo() { + | +++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/generalize/occurs-check-nested-alias.next.stderr b/tests/ui/traits/next-solver/generalize/occurs-check-nested-alias.next.stderr index ad8b24a39c701..aaadf604a80ac 100644 --- a/tests/ui/traits/next-solver/generalize/occurs-check-nested-alias.next.stderr +++ b/tests/ui/traits/next-solver/generalize/occurs-check-nested-alias.next.stderr @@ -1,11 +1,9 @@ -error[E0275]: overflow evaluating the requirement `<>::Id as Unnormalizable>::Assoc == _` +error[E0284]: type annotations needed: cannot satisfy `<>::Id as Unnormalizable>::Assoc == _` --> $DIR/occurs-check-nested-alias.rs:36:9 | LL | x = y; - | ^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`occurs_check_nested_alias`) + | ^ cannot satisfy `<>::Id as Unnormalizable>::Assoc == _` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/traits/next-solver/normalize-param-env-2.rs b/tests/ui/traits/next-solver/normalize-param-env-2.rs index ce084651bfb6e..9da1f8dbec1de 100644 --- a/tests/ui/traits/next-solver/normalize-param-env-2.rs +++ b/tests/ui/traits/next-solver/normalize-param-env-2.rs @@ -1,24 +1,29 @@ -// check-pass // compile-flags: -Znext-solver -// Issue 92505 +// known-bug: #92505 +// When checking that the impl method where-bounds are implied by the trait, +// we prove `<() as A>::Assoc: A` in the environment `<() as A>::Assoc: A`. +// +// Normalizing `<() as A>::Assoc` is ambiguous in that environment. The +// where-bound `<() as A>::Assoc: A` may apply, resulting in overflow. trait A { - type I; + type Assoc; fn f() where - Self::I: A, + Self::Assoc: A, { } } impl A for () { - type I = (); + type Assoc = (); fn f() where - Self::I: A, + Self::Assoc: A, { + <() as A>::f(); } } diff --git a/tests/ui/traits/next-solver/normalize-param-env-2.stderr b/tests/ui/traits/next-solver/normalize-param-env-2.stderr new file mode 100644 index 0000000000000..a52022e539eb7 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize-param-env-2.stderr @@ -0,0 +1,35 @@ +error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: A` + --> $DIR/normalize-param-env-2.rs:24:22 + | +LL | Self::Assoc: A, + | ^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`) +note: the requirement `<() as A>::Assoc: A` appears on the `impl`'s method `f` but not on the corresponding trait's method + --> $DIR/normalize-param-env-2.rs:12:8 + | +LL | trait A { + | - in this trait +... +LL | fn f() + | ^ this trait's method doesn't have the requirement `<() as A>::Assoc: A` + +error[E0275]: overflow evaluating the requirement `<() as A>::Assoc: A` + --> $DIR/normalize-param-env-2.rs:24:22 + | +LL | Self::Assoc: A, + | ^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`) + +error[E0275]: overflow evaluating the requirement `<() as A>::Assoc well-formed` + --> $DIR/normalize-param-env-2.rs:24:22 + | +LL | Self::Assoc: A, + | ^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`) + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/normalize-param-env-4.next.stderr b/tests/ui/traits/next-solver/normalize-param-env-4.next.stderr new file mode 100644 index 0000000000000..dec820c61b06d --- /dev/null +++ b/tests/ui/traits/next-solver/normalize-param-env-4.next.stderr @@ -0,0 +1,19 @@ +error[E0275]: overflow evaluating the requirement `::Assoc: Trait` + --> $DIR/normalize-param-env-4.rs:18:26 + | +LL | ::Assoc: Trait, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_4`) + +error[E0275]: overflow evaluating the requirement `::Assoc well-formed` + --> $DIR/normalize-param-env-4.rs:18:26 + | +LL | ::Assoc: Trait, + | ^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_4`) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/normalize-param-env-4.rs b/tests/ui/traits/next-solver/normalize-param-env-4.rs new file mode 100644 index 0000000000000..d49f74922971f --- /dev/null +++ b/tests/ui/traits/next-solver/normalize-param-env-4.rs @@ -0,0 +1,34 @@ +// revisions: current next +//[next] compile-flags: -Znext-solver +//[next] known-bug: #92505 +//[current] check-pass + +trait Trait { + type Assoc; +} + +impl Trait for T { + type Assoc = T; +} + +fn impls_trait() {} + +fn foo() +where + ::Assoc: Trait, +{ + // Trying to use `::Assoc: Trait` to prove `T: Trait` + // requires normalizing `::Assoc`. We do not normalize + // using impl candidates if there's a where-bound for that trait. + // + // We therefore check whether `T: Trait` is proven by the environment. + // For that we try to apply the `::Assoc: Trait` candidate, + // trying to normalize its self type results in overflow. + // + // In the old solver we eagerly normalize the environment, ignoring the + // unnormalized `::Assoc: Trait` where-bound when normalizing + // `::Asosc` + impls_trait::(); +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.rs b/tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.rs deleted file mode 100644 index 7dc87daccd98b..0000000000000 --- a/tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.rs +++ /dev/null @@ -1,40 +0,0 @@ -// [no_self_infer] check-pass -// compile-flags: -Znext-solver -// revisions: self_infer no_self_infer - -// checks that the new solver is smart enough to infer `?0 = U` when solving: -// `normalizes-to( as Trait>::Assoc, u8)` -// with `normalizes-to( as Trait>::Assoc, u8)` in the paramenv even when -// there is a separate `Vec: Trait` bound in the paramenv. -// -// FIXME(-Znext-solver) -// This could also compile for `normalizes-to(::Assoc, u8)` but -// we currently immediately consider a goal ambiguous if the self type is an -// inference variable. - -trait Trait { - type Assoc; -} - -fn foo>(x: T) {} - -#[cfg(self_infer)] -fn unconstrained() -> T { - todo!() -} - -#[cfg(no_self_infer)] -fn unconstrained() -> Vec { - todo!() -} - -fn bar() -where - Vec: Trait, - Vec: Trait, -{ - foo(unconstrained()) - //[self_infer]~^ ERROR type annotations needed -} - -fn main() {} diff --git a/tests/ui/traits/next-solver/param-candidate-doesnt-shadow-project.rs b/tests/ui/traits/next-solver/param-candidate-doesnt-shadow-project.rs deleted file mode 100644 index f67b073c53c5c..0000000000000 --- a/tests/ui/traits/next-solver/param-candidate-doesnt-shadow-project.rs +++ /dev/null @@ -1,25 +0,0 @@ -// compile-flags: -Znext-solver -// check-pass - -trait Foo { - type Assoc; -} - -trait Bar {} - -impl Foo for T { - type Assoc = i32; -} - -impl Bar for T where T: Foo {} - -fn require_bar() {} - -fn foo() { - // Unlike the classic solver, `::Assoc = _` will still project - // down to `i32` even though there's a param-env candidate here, since we - // don't assemble any param-env projection candidates for `T: Foo` alone. - require_bar::(); -} - -fn main() {}