Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Expand Down
22 changes: 22 additions & 0 deletions tests/ui/typeck/method-chain-gats.rs
Original file line number Diff line number Diff line change
@@ -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() {}
27 changes: 27 additions & 0 deletions tests/ui/typeck/method-chain-gats.stderr
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member Author

@fmease fmease Mar 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrong location I suppose?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the location is "right", because the logic is looking for the root element of the method chain (even accounting for let bindings and fn arguments), so this is trying to say that <Base>::Base<_> = <T as Base>::Base<_>, which is somewhat useless information.

I do have to say that the test code makes my head spin, even without accounting for the error message 😬

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the only thing left to do is to improve this label to better account for GATs, but that shouldn't happen in this PR.

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