diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 00a2adccf64a5..7c1bfebef2cd0 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 16290c8dbcafb..de834b8fb64a0 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 `::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 { // `` 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 0000000000000..c7081c9a3b167 --- /dev/null +++ b/tests/ui/typeck/method-chain-gats.rs @@ -0,0 +1,22 @@ +// Regression test for issue #121898. + +trait Base { + type Base; +} + +trait Functor: Base { + fn fmap(self, f: impl Fn(A) -> B) -> Self::Base + where + Self::Base: Functor; +} + +fn fmap2(input: T, f1: impl Fn(A) -> B, f2: impl Fn(B) -> C) -> T::Base +where + T: Functor, + T::Base: Functor = T::Base>, +{ + input.fmap(f1).fmap(f2) + //~^ ERROR the trait bound `::Base: Functor` 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 0000000000000..6338379221471 --- /dev/null +++ b/tests/ui/typeck/method-chain-gats.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `::Base: Functor` is not satisfied + --> $DIR/method-chain-gats.rs:18:20 + | +LL | input.fmap(f1).fmap(f2) + | ^^^^ the trait `Functor` is not implemented for `::Base` + | +note: the method call chain might not have had the expected associated types + --> $DIR/method-chain-gats.rs:13:29 + | +LL | fn fmap2(input: T, f1: impl Fn(A) -> B, f2: impl Fn(B) -> C) -> T::Base + | ^ `Base::Base` is `::Base<_>` here +note: required by a bound in `Functor::fmap` + --> $DIR/method-chain-gats.rs:10:24 + | +LL | fn fmap(self, f: impl Fn(A) -> B) -> Self::Base + | ---- required by a bound in this associated function +LL | where +LL | Self::Base: Functor; + | ^^^^^^^^^^ required by this bound in `Functor::fmap` +help: consider further restricting the associated type + | +LL | T::Base: Functor = T::Base>, ::Base: Functor + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`.