about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs29
-rw-r--r--tests/ui/typeck/method-chain-gats.rs22
-rw-r--r--tests/ui/typeck/method-chain-gats.stderr27
4 files changed, 63 insertions, 16 deletions
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index 00a2adccf64..7c1bfebef2c 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -16,6 +16,7 @@
 #![allow(internal_features)]
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
+#![feature(assert_matches)]
 #![feature(associated_type_bounds)]
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 16290c8dbca..de834b8fb64 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -38,6 +38,7 @@ use rustc_span::def_id::LocalDefId;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP};
 use rustc_target::spec::abi;
+use std::assert_matches::debug_assert_matches;
 use std::borrow::Cow;
 use std::iter;
 
@@ -4219,30 +4220,25 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             };
 
             let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span };
-            let trait_def_id = proj.trait_def_id(self.tcx);
             // Make `Self` be equivalent to the type of the call chain
             // expression we're looking at now, so that we can tell what
             // for example `Iterator::Item` is at this point in the chain.
-            let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| {
-                match param.kind {
-                    ty::GenericParamDefKind::Type { .. } => {
-                        if param.index == 0 {
-                            return prev_ty.into();
-                        }
-                    }
-                    ty::GenericParamDefKind::Lifetime | ty::GenericParamDefKind::Const { .. } => {}
+            let args = GenericArgs::for_item(self.tcx, proj.def_id, |param, _| {
+                if param.index == 0 {
+                    debug_assert_matches!(param.kind, ty::GenericParamDefKind::Type { .. });
+                    return prev_ty.into();
                 }
                 self.var_for_def(span, param)
             });
             // This will hold the resolved type of the associated type, if the
             // current expression implements the trait that associated type is
             // in. For example, this would be what `Iterator::Item` is here.
-            let ty_var = self.infcx.next_ty_var(origin);
+            let ty = self.infcx.next_ty_var(origin);
             // This corresponds to `<ExprTy as Iterator>::Item = _`.
             let projection = ty::Binder::dummy(ty::PredicateKind::Clause(
                 ty::ClauseKind::Projection(ty::ProjectionPredicate {
                     projection_ty: ty::AliasTy::new(self.tcx, proj.def_id, args),
-                    term: ty_var.into(),
+                    term: ty.into(),
                 }),
             ));
             let body_def_id = self.tcx.hir().enclosing_body_owner(body_id);
@@ -4254,14 +4250,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 param_env,
                 projection,
             ));
-            if ocx.select_where_possible().is_empty() {
-                // `ty_var` now holds the type that `Item` is for `ExprTy`.
-                let ty_var = self.resolve_vars_if_possible(ty_var);
-                assocs_in_this_method.push(Some((span, (proj.def_id, ty_var))));
+            if ocx.select_where_possible().is_empty()
+                && let ty = self.resolve_vars_if_possible(ty)
+                && !ty.is_ty_var()
+            {
+                assocs_in_this_method.push(Some((span, (proj.def_id, ty))));
             } else {
                 // `<ExprTy as Iterator>` didn't select, so likely we've
                 // reached the end of the iterator chain, like the originating
-                // `Vec<_>`.
+                // `Vec<_>` or the `ty` couldn't be determined.
                 // Keep the space consistent for later zipping.
                 assocs_in_this_method.push(None);
             }
diff --git a/tests/ui/typeck/method-chain-gats.rs b/tests/ui/typeck/method-chain-gats.rs
new file mode 100644
index 00000000000..c7081c9a3b1
--- /dev/null
+++ b/tests/ui/typeck/method-chain-gats.rs
@@ -0,0 +1,22 @@
+// Regression test for issue #121898.
+
+trait Base {
+    type Base<B>;
+}
+
+trait Functor<A>: Base {
+    fn fmap<B>(self, f: impl Fn(A) -> B) -> Self::Base<B>
+    where
+        Self::Base<B>: Functor<B>;
+}
+
+fn fmap2<T, A, B, C>(input: T, f1: impl Fn(A) -> B, f2: impl Fn(B) -> C) -> T::Base<C>
+where
+    T: Functor<A>,
+    T::Base<B>: Functor<B, Base<C> = T::Base<C>>,
+{
+    input.fmap(f1).fmap(f2)
+    //~^ ERROR the trait bound `<T as Base>::Base<C>: Functor<C>` is not satisfied
+}
+
+fn main() {}
diff --git a/tests/ui/typeck/method-chain-gats.stderr b/tests/ui/typeck/method-chain-gats.stderr
new file mode 100644
index 00000000000..63383792214
--- /dev/null
+++ b/tests/ui/typeck/method-chain-gats.stderr
@@ -0,0 +1,27 @@
+error[E0277]: the trait bound `<T as Base>::Base<C>: Functor<C>` is not satisfied
+  --> $DIR/method-chain-gats.rs:18:20
+   |
+LL |     input.fmap(f1).fmap(f2)
+   |                    ^^^^ the trait `Functor<C>` is not implemented for `<T as Base>::Base<C>`
+   |
+note: the method call chain might not have had the expected associated types
+  --> $DIR/method-chain-gats.rs:13:29
+   |
+LL | fn fmap2<T, A, B, C>(input: T, f1: impl Fn(A) -> B, f2: impl Fn(B) -> C) -> T::Base<C>
+   |                             ^ `Base::Base` is `<T as Base>::Base<_>` here
+note: required by a bound in `Functor::fmap`
+  --> $DIR/method-chain-gats.rs:10:24
+   |
+LL |     fn fmap<B>(self, f: impl Fn(A) -> B) -> Self::Base<B>
+   |        ---- required by a bound in this associated function
+LL |     where
+LL |         Self::Base<B>: Functor<B>;
+   |                        ^^^^^^^^^^ required by this bound in `Functor::fmap`
+help: consider further restricting the associated type
+   |
+LL |     T::Base<B>: Functor<B, Base<C> = T::Base<C>>, <T as Base>::Base<C>: Functor<C>
+   |                                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.