Skip to content

Commit

Permalink
Implement consecutive shorthand projections
Browse files Browse the repository at this point in the history
  • Loading branch information
fmease committed Jun 19, 2024
1 parent 737e423 commit 6fc5c3c
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 43 deletions.
40 changes: 37 additions & 3 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1070,13 +1070,46 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
)?
}
(
&ty::Param(_),
Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
ty::Param(_),
Res::SelfTyParam { trait_: param_def_id }
| Res::Def(DefKind::TyParam, param_def_id),
) => self.probe_single_ty_param_bound_for_assoc_ty(
param_did.expect_local(),
param_def_id.expect_local(),
assoc_ident,
span,
)?,
// FIXME(fmease):
// Should we require the pre-lowered projectee (the HIR QSelf) to be a `Res::Def(DefKind::AssocTy, _)`?
// Rephrased, should we flat out reject `Identity<T::Assoc>::Assoc` even if `T::Assoc::Assoc` typeck's?
// For comparison, `Identity<T>::Assoc` gets rejected even if `T::Assoc` passes typeck.
//
// If we want to do so, it gets a bit tricky because unfortunately, for type-relative paths,
// the `Res` never gets updated from `Res::Err` to `Res::Def(AssocTy, _)`.
//
// FIXME(fmease): Add UI tests for `feature(more_qualified_paths)` (e.g., `T::A::B { … }`).
(ty::Alias(ty::Projection, alias_ty), _res) => {
// FIXME: Double-check that this doesn't lead to any unwanted cycle errors.
let predicates = tcx.item_bounds(alias_ty.def_id).instantiate(tcx, alias_ty.args);

self.probe_single_bound_for_assoc_item(
|| {
let trait_refs = predicates.iter().filter_map(|pred| {
pred.as_trait_clause().map(|t| t.map_bound(|t| t.trait_ref))
});
traits::transitive_bounds_that_define_assoc_item(
tcx,
trait_refs,
assoc_ident,
)
},
qself_ty,
None,
ty::AssocKind::Type,
assoc_ident,
span,
None,
)?
}
_ => {
let reported = if variant_resolution.is_some() {
// Variant in type position
Expand Down Expand Up @@ -1180,6 +1213,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
);
});
}

Ok((ty, DefKind::AssocTy, assoc_ty.def_id))
}

Expand Down
1 change: 0 additions & 1 deletion src/tools/tidy/src/issues.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1855,7 +1855,6 @@ ui/issues/issue-23024.rs
ui/issues/issue-23036.rs
ui/issues/issue-23041.rs
ui/issues/issue-23046.rs
ui/issues/issue-23073.rs
ui/issues/issue-2311-2.rs
ui/issues/issue-2311.rs
ui/issues/issue-2312.rs
Expand Down
2 changes: 1 addition & 1 deletion src/tools/tidy/src/ui_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use std::path::{Path, PathBuf};
const ENTRY_LIMIT: u32 = 900;
// FIXME: The following limits should be reduced eventually.

const ISSUES_ENTRY_LIMIT: u32 = 1672;
const ISSUES_ENTRY_LIMIT: u32 = 1670;

const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
"rs", // test source files
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
trait ChooseMe {
type Type;
}

trait PickMe {
type Type;
}

trait HaveItAll {
type See: ChooseMe + PickMe;
}

struct Env<T: HaveItAll>(T::See::Type);
//~^ ERROR ambiguous associated type `Type` in bounds of `<T as HaveItAll>::See`

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0221]: ambiguous associated type `Type` in bounds of `<T as HaveItAll>::See`
--> $DIR/consecutive-shorthand-projections-ambiguous.rs:13:26
|
LL | type Type;
| --------- ambiguous `Type` from `ChooseMe`
...
LL | type Type;
| --------- ambiguous `Type` from `PickMe`
...
LL | struct Env<T: HaveItAll>(T::See::Type);
| ^^^^^^^^^^^^ ambiguous associated type `Type`
|
help: use fully-qualified syntax to disambiguate
|
LL | struct Env<T: HaveItAll>(<<T as HaveItAll>::See as ChooseMe>::Type);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
help: use fully-qualified syntax to disambiguate
|
LL | struct Env<T: HaveItAll>(<<T as HaveItAll>::See as PickMe>::Type);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0221`.
75 changes: 75 additions & 0 deletions tests/ui/associated-types/consecutive-shorthand-projections.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//@ check-pass

fn factory0<T: Factory>() {
let _: T::Output::Category;
}

fn factory1<T: Factory<Output: Product<Category = u16>>>(category: u16) {
let _: T::Output::Category = category;
}

fn factory2<T: Factory>(_: T::Output::Category) {}

trait Factory {
type Output: Product;
}

impl Factory for () {
type Output = u128;
}

trait Product {
type Category;
}

impl Product for u128 {
type Category = u16;
}

/////////////////////////

fn chain<C: Chain<Link = C>>(chain: C) {
let _: C::Link::Link::Link::Link::Link = chain;
}

trait Chain { type Link: Chain; }

impl Chain for () {
type Link = Self;
}

/////////////////////////

fn scope<'r, T: Main<'static, (i32, U), 1>, U, const Q: usize>() {
let _: T::Next<'r, (), Q>::Final;
}

trait Main<'a, T, const N: usize> {
type Next<'b, U, const M: usize>: Aux<'a, 'b, (T, U), N, M>;
}

impl<'a, T, const N: usize> Main<'a, T, N> for () {
type Next<'_b, _U, const _M: usize> = ();
}

trait Aux<'a, 'b, T, const N: usize, const M: usize> {
type Final;
}

impl<'a, 'b, T, const N: usize, const M: usize> Aux<'a, 'b, T, N, M> for () {
type Final = [[(T, &'a (), &'b ()); N]; M];
}

/////////////////////////

fn main() {
factory0::<()>();
factory1::<()>(360);
factory2::<()>(720);
let _: <() as Factory>::Output::Category;

chain(());
let _: <() as Chain>::Link::Link::Link;

scope::<(), bool, 32>();
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
//@ check-pass

#![feature(associated_type_defaults)]

trait Foo { type T; }

trait Bar {
type Foo: Foo;
type FooT = <<Self as Bar>::Foo>::T; //~ ERROR ambiguous associated type
type FooT = <<Self as Bar>::Foo>::T;
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// FIXME(fmease): Should we allow this as part of this MVP?
// Of course, under #22519 (arbitrary shorthand projections), this should obviously typeck.
// For reference, `T::Alias` typeck'ing does *not* imply `Identity<T>::Alias` typeck'ing.
//@ check-pass

type Identity<T> = T;

trait Trait {
type Project: Trait;
}

fn scope<T: Trait>() {
let _: Identity<T::Project>::Project;
}

fn main() {}
14 changes: 0 additions & 14 deletions tests/ui/issues/issue-23073.stderr

This file was deleted.

4 changes: 1 addition & 3 deletions tests/ui/qualified/qualified-path-params-2.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Check that qualified paths with type parameters
// fail during type checking and not during parsing

struct S;

trait Tr {
Expand All @@ -15,7 +14,6 @@ impl S {
fn f<T>() {}
}

type A = <S as Tr>::A::f<u8>;
//~^ ERROR ambiguous associated type
type A = <S as Tr>::A::f<u8>; //~ ERROR associated type `f` not found for `<S as Tr>::A`

fn main() {}
13 changes: 4 additions & 9 deletions tests/ui/qualified/qualified-path-params-2.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
error[E0223]: ambiguous associated type
--> $DIR/qualified-path-params-2.rs:18:10
error[E0220]: associated type `f` not found for `<S as Tr>::A`
--> $DIR/qualified-path-params-2.rs:17:24
|
LL | type A = <S as Tr>::A::f<u8>;
| ^^^^^^^^^^^^^^^^^^^
|
help: if there were a trait named `Example` with associated type `f` implemented for `<S as Tr>::A`, you could use the fully-qualified path
|
LL | type A = <<S as Tr>::A as Example>::f;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ^ there is a similarly named associated type `A` in the trait `Tr`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0223`.
For more information about this error, try `rustc --explain E0220`.
2 changes: 1 addition & 1 deletion tests/ui/ufcs/ufcs-partially-resolved.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ fn main() {
<u8 as Tr>::N::NN; //~ ERROR cannot find associated type `N` in trait `Tr`
<u8 as E>::N::NN; //~ ERROR expected trait, found enum `E`
<u8 as A>::N::NN; //~ ERROR expected trait, found type alias `A`
let _: <u8 as Tr>::Y::NN; //~ ERROR ambiguous associated type
let _: <u8 as Tr>::Y::NN; //~ ERROR associated type `NN` not found for `<u8 as Tr>::Y`
let _: <u8 as E>::Y::NN; //~ ERROR expected trait, found enum `E`
<u8 as Tr>::Y::NN; //~ ERROR no associated item named `NN` found for type `u16`
<u8 as E>::Y::NN; //~ ERROR expected trait, found enum `E`
Expand Down
15 changes: 5 additions & 10 deletions tests/ui/ufcs/ufcs-partially-resolved.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -245,16 +245,11 @@ LL | let _: <u8 as Dr>::Z::N;
| |
| help: an associated type with a similar name exists: `X`

error[E0223]: ambiguous associated type
--> $DIR/ufcs-partially-resolved.rs:36:12
error[E0220]: associated type `NN` not found for `<u8 as Tr>::Y`
--> $DIR/ufcs-partially-resolved.rs:36:27
|
LL | let _: <u8 as Tr>::Y::NN;
| ^^^^^^^^^^^^^^^^^
|
help: if there were a trait named `Example` with associated type `NN` implemented for `<u8 as Tr>::Y`, you could use the fully-qualified path
|
LL | let _: <<u8 as Tr>::Y as Example>::NN;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ^^ associated type `NN` not found

error[E0599]: no associated item named `NN` found for type `u16` in the current scope
--> $DIR/ufcs-partially-resolved.rs:38:20
Expand All @@ -270,5 +265,5 @@ LL | <u8 as Dr>::X::N;

error: aborting due to 32 previous errors

Some errors have detailed explanations: E0223, E0404, E0405, E0575, E0576, E0599.
For more information about an error, try `rustc --explain E0223`.
Some errors have detailed explanations: E0220, E0404, E0405, E0575, E0576, E0599.
For more information about an error, try `rustc --explain E0220`.

0 comments on commit 6fc5c3c

Please sign in to comment.