From b8186470e631828e2fdbcb95e4680dc6d200a312 Mon Sep 17 00:00:00 2001 From: Raushan kumar Date: Sat, 27 Jun 2026 14:53:13 +0000 Subject: [PATCH 1/2] test(traits): add baseline test for conditionally implemented traits --- .../conditionally-implemented-trait-158423.rs | 28 +++++++++++++++++++ ...ditionally-implemented-trait-158423.stderr | 27 ++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/ui/traits/error_reporting/conditionally-implemented-trait-158423.rs create mode 100644 tests/ui/traits/error_reporting/conditionally-implemented-trait-158423.stderr diff --git a/tests/ui/traits/error_reporting/conditionally-implemented-trait-158423.rs b/tests/ui/traits/error_reporting/conditionally-implemented-trait-158423.rs new file mode 100644 index 0000000000000..3f95e6a7a0da2 --- /dev/null +++ b/tests/ui/traits/error_reporting/conditionally-implemented-trait-158423.rs @@ -0,0 +1,28 @@ +struct Test(i32, i64); +trait Foo<'a> { + type Assoc; +} + +trait SimpleFoo { + type SimpleAssoc; +} +impl<'a, T> Foo<'a> for T where T: SimpleFoo { + type Assoc = T::SimpleAssoc; +} + +impl SimpleFoo for i32 { + type SimpleAssoc = i32; +} +impl SimpleFoo for i64 { + type SimpleAssoc = i32; +} + +impl<'a> Foo<'a> for Test where i32: Foo<'a, Assoc = i32>, i64: Foo<'a, Assoc = i64> { + type Assoc = Test; +} + +fn process<'a, T: Foo<'a>>(_input: T) {} +fn test() { process(Test(0, 1)) } +//~^ ERROR the trait bound `Test: Foo<'_>` is not satisfied + +fn main() {} diff --git a/tests/ui/traits/error_reporting/conditionally-implemented-trait-158423.stderr b/tests/ui/traits/error_reporting/conditionally-implemented-trait-158423.stderr new file mode 100644 index 0000000000000..62146a4fd2607 --- /dev/null +++ b/tests/ui/traits/error_reporting/conditionally-implemented-trait-158423.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `Test: Foo<'_>` is not satisfied + --> $DIR/conditionally-implemented-trait-158423.rs:25:21 + | +LL | fn test() { process(Test(0, 1)) } + | ------- ^^^^^^^^^^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | +help: the trait `Foo<'_>` is not implemented for `Test` + --> $DIR/conditionally-implemented-trait-158423.rs:1:1 + | +LL | struct Test(i32, i64); + | ^^^^^^^^^^^ +help: the trait `Foo<'_>` is implemented for `Test` + --> $DIR/conditionally-implemented-trait-158423.rs:20:1 + | +LL | impl<'a> Foo<'a> for Test where i32: Foo<'a, Assoc = i32>, i64: Foo<'a, Assoc = i64> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `process` + --> $DIR/conditionally-implemented-trait-158423.rs:24:19 + | +LL | fn process<'a, T: Foo<'a>>(_input: T) {} + | ^^^^^^^ required by this bound in `process` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From 4613bf932a7cc9e5bf36985e3dc2532de97ab7c9 Mon Sep 17 00:00:00 2001 From: Raushan kumar Date: Sat, 27 Jun 2026 16:42:17 +0000 Subject: [PATCH 2/2] fix(traits): clarify conditional impl diagnostics --- .../traits/fulfillment_errors.rs | 66 ++++++++++++++++++- ...undant-derive-note-on-unimplemented.stderr | 4 +- .../blame-trait-error.stderr | 6 +- tests/ui/impl-trait/nested_impl_trait.stderr | 10 ++- .../copy-bounds-impl-type-params.stderr | 12 ++-- ...ditionally-implemented-trait-158423.stderr | 6 +- ...incompleteness-unstable-result.with.stderr | 3 +- ...ompleteness-unstable-result.without.stderr | 3 +- 8 files changed, 95 insertions(+), 15 deletions(-) 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 973a088a7f5d4..8cc2ff9fa70e0 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 @@ -2326,18 +2326,80 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { return false; } + let mut multi_span = MultiSpan::from_span(self.tcx.def_span(def_id)); let (desc, mention_castable) = match (cand.self_ty().kind(), trait_pred.self_ty().skip_binder().kind()) { (ty::FnPtr(..), ty::FnDef(..)) => { (" implemented for fn pointer `", ", cast using `as`") } (ty::FnPtr(..), _) => (" implemented for fn pointer `", ""), - _ => (" implemented for `", ""), + _ => { + let evaluate_obligations = || { + let ocx = ObligationCtxt::new_with_diagnostics(self); + self.enter_forall(trait_pred, |obligation_trait_ref| { + let impl_args = self.fresh_args_for_item(DUMMY_SP, def_id); + let impl_trait_ref = ocx.normalize( + &ObligationCause::dummy(), + param_env, + ty::EarlyBinder::bind(self.tcx, cand) + .instantiate(self.tcx, impl_args), + ); + if ocx + .eq( + &ObligationCause::dummy(), + param_env, + obligation_trait_ref.trait_ref, + impl_trait_ref, + ) + .is_err() + { + return Vec::new(); + } + ocx.register_obligations( + self.tcx + .predicates_of(def_id) + .instantiate(self.tcx, impl_args) + .into_iter() + .map(|(clause, span)| { + Obligation::new( + self.tcx, + ObligationCause::dummy_with_span(span), + param_env, + clause.skip_normalization(), + ) + }), + ); + ocx.try_evaluate_obligations() + }) + }; + let failing_obligations = + if !self.tcx.predicates_of(def_id).predicates.is_empty() { + self.probe(|_| evaluate_obligations()) + } else { + Vec::new() + }; + + if failing_obligations.is_empty() { + (" implemented for `", "") + } else { + for error in failing_obligations { + multi_span.push_span_label( + error.root_obligation.cause.span, + format!( + "unsatisfied requirement introduced here: `{}`", + error.root_obligation.predicate, + ), + ); + } + + (" conditionally implemented for `", "") + } + } }; let trait_ = self.tcx.short_string(cand.print_trait_sugared(), err.long_ty_path()); let self_ty = self.tcx.short_string(cand.self_ty(), err.long_ty_path()); err.highlighted_span_help( - self.tcx.def_span(def_id), + multi_span, vec![ StringPart::normal(format!("the trait `{trait_}` ")), StringPart::highlighted("is"), diff --git a/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr b/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr index dcb1639593a58..09b45212c3c9a 100644 --- a/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr +++ b/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr @@ -7,11 +7,13 @@ LL | println!("{:?}", S(X)); | required by this formatting parameter | = help: the trait `Debug` is not implemented for `X` -help: the trait `Debug` is implemented for `S` +help: the trait `Debug` is conditionally implemented for `S` --> $DIR/redundant-derive-note-on-unimplemented.rs:8:10 | LL | #[derive(Debug)] | ^^^^^ +LL | struct S(T); + | - unsatisfied requirement introduced here: `X: Debug` note: required for `S` to implement `Debug` --> $DIR/redundant-derive-note-on-unimplemented.rs:9:8 | diff --git a/tests/ui/errors/trait-bound-error-spans/blame-trait-error.stderr b/tests/ui/errors/trait-bound-error-spans/blame-trait-error.stderr index a28b28c0fe16c..b301f5f6bc09b 100644 --- a/tests/ui/errors/trait-bound-error-spans/blame-trait-error.stderr +++ b/tests/ui/errors/trait-bound-error-spans/blame-trait-error.stderr @@ -39,11 +39,13 @@ LL | want(Some(())); | required by a bound introduced by this call | = help: the trait `Iterator` is not implemented for `()` -help: the trait `T1` is implemented for `Option` +help: the trait `T1` is conditionally implemented for `Option` --> $DIR/blame-trait-error.rs:21:1 | LL | impl T1 for Option {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^--------^^^^^^^^^^^^^^^^^^^ + | | + | unsatisfied requirement introduced here: `(): Iterator` note: required for `Option<()>` to implement `T1` --> $DIR/blame-trait-error.rs:21:20 | diff --git a/tests/ui/impl-trait/nested_impl_trait.stderr b/tests/ui/impl-trait/nested_impl_trait.stderr index 406f21941dc97..91b38b5176516 100644 --- a/tests/ui/impl-trait/nested_impl_trait.stderr +++ b/tests/ui/impl-trait/nested_impl_trait.stderr @@ -50,8 +50,11 @@ LL | fn bad_in_ret_position(x: impl Into) -> impl Into { x } | | | the trait `From>` is not implemented for `impl Debug` | -help: the trait `Into` is implemented for `T` +help: the trait `Into` is conditionally implemented for `T` --> $SRC_DIR/core/src/convert/mod.rs:LL:COL + ::: $SRC_DIR/core/src/convert/mod.rs:LL:COL + | + = note: unsatisfied requirement introduced here: `impl Debug: From>` = note: required for `impl Into` to implement `Into` error[E0277]: the trait bound `impl Debug: From>` is not satisfied @@ -62,8 +65,11 @@ LL | fn bad(x: impl Into) -> impl Into { x } | | | the trait `From>` is not implemented for `impl Debug` | -help: the trait `Into` is implemented for `T` +help: the trait `Into` is conditionally implemented for `T` --> $SRC_DIR/core/src/convert/mod.rs:LL:COL + ::: $SRC_DIR/core/src/convert/mod.rs:LL:COL + | + = note: unsatisfied requirement introduced here: `impl Debug: From>` = note: required for `impl Into` to implement `Into` error: aborting due to 7 previous errors diff --git a/tests/ui/traits/copy-bounds-impl-type-params.stderr b/tests/ui/traits/copy-bounds-impl-type-params.stderr index 08fde8fb5df35..4f9600f3f321d 100644 --- a/tests/ui/traits/copy-bounds-impl-type-params.stderr +++ b/tests/ui/traits/copy-bounds-impl-type-params.stderr @@ -80,11 +80,13 @@ error[E0277]: the trait bound `String: Copy` is not satisfied LL | let a = t as Box>; | ^ the trait `Copy` is not implemented for `String` | -help: the trait `Gettable` is implemented for `S` +help: the trait `Gettable` is conditionally implemented for `S` --> $DIR/copy-bounds-impl-type-params.rs:14:1 | LL | impl Gettable for S {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | unsatisfied requirement introduced here: `String: Copy` note: required for `S` to implement `Gettable` --> $DIR/copy-bounds-impl-type-params.rs:14:32 | @@ -100,11 +102,13 @@ error[E0277]: the trait bound `Foo: Copy` is not satisfied LL | let a: Box> = t; | ^ the trait `Copy` is not implemented for `Foo` | -help: the trait `Gettable` is implemented for `S` +help: the trait `Gettable` is conditionally implemented for `S` --> $DIR/copy-bounds-impl-type-params.rs:14:1 | LL | impl Gettable for S {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | unsatisfied requirement introduced here: `Foo: Copy` note: required for `S` to implement `Gettable` --> $DIR/copy-bounds-impl-type-params.rs:14:32 | diff --git a/tests/ui/traits/error_reporting/conditionally-implemented-trait-158423.stderr b/tests/ui/traits/error_reporting/conditionally-implemented-trait-158423.stderr index 62146a4fd2607..b6c6329abc84b 100644 --- a/tests/ui/traits/error_reporting/conditionally-implemented-trait-158423.stderr +++ b/tests/ui/traits/error_reporting/conditionally-implemented-trait-158423.stderr @@ -11,11 +11,13 @@ help: the trait `Foo<'_>` is not implemented for `Test` | LL | struct Test(i32, i64); | ^^^^^^^^^^^ -help: the trait `Foo<'_>` is implemented for `Test` +help: the trait `Foo<'_>` is conditionally implemented for `Test` --> $DIR/conditionally-implemented-trait-158423.rs:20:1 | LL | impl<'a> Foo<'a> for Test where i32: Foo<'a, Assoc = i32>, i64: Foo<'a, Assoc = i64> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^ + | | + | unsatisfied requirement introduced here: `>::Assoc == i64` note: required by a bound in `process` --> $DIR/conditionally-implemented-trait-158423.rs:24:19 | diff --git a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.with.stderr b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.with.stderr index 9b41a88615e4a..4f209fab4c23c 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.with.stderr +++ b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.with.stderr @@ -9,13 +9,14 @@ help: the trait `Trait<_, _, _>` is not implemented for `A` | LL | struct A(*const T); | ^^^^^^^^^^^ -help: the trait `Trait` is implemented for `A` +help: the trait `Trait` is conditionally implemented for `A` --> $DIR/incompleteness-unstable-result.rs:34:1 | LL | / impl Trait for A LL | | where LL | | T: IncompleteGuidance, LL | | A: Trait, + | | -------------- unsatisfied requirement introduced here: `A<_>: Trait<_, _, _>` LL | | B: Trait, LL | | (): ToU8, | |________________^ diff --git a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.without.stderr b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.without.stderr index 9b41a88615e4a..4f209fab4c23c 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.without.stderr +++ b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.without.stderr @@ -9,13 +9,14 @@ help: the trait `Trait<_, _, _>` is not implemented for `A` | LL | struct A(*const T); | ^^^^^^^^^^^ -help: the trait `Trait` is implemented for `A` +help: the trait `Trait` is conditionally implemented for `A` --> $DIR/incompleteness-unstable-result.rs:34:1 | LL | / impl Trait for A LL | | where LL | | T: IncompleteGuidance, LL | | A: Trait, + | | -------------- unsatisfied requirement introduced here: `A<_>: Trait<_, _, _>` LL | | B: Trait, LL | | (): ToU8, | |________________^