Rollup merge of #121912 - fmease:diag-method-chains-gat, r=compiler-errors,estebank

Properly deal with GATs when looking for method chains to point at

Fixes #121898.

~~While it prevents an ICE and the structured suggestion is correct, the method chain diagnostic notes are weird / useless / incorrect judging by a quick look. I guess I should improve that in this PR.~~ Sufficiently taken care of.

r? estebank or compiler-errors (#105332, #105674).
This commit is contained in:
Matthias Krüger 2024-03-04 07:57:56 +01:00 committed by GitHub
commit cd9e5b5f43
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 63 additions and 16 deletions

View File

@ -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)]

View File

@ -38,6 +38,7 @@
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 @@ fn probe_assoc_types_at_expr(
};
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 @@ fn probe_assoc_types_at_expr(
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);
}

View File

@ -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() {}

View File

@ -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`.