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

Implement Return Type Notation (RTN)'s path form in where clauses #129629

Merged
merged 7 commits into from
Sep 22, 2024

Conversation

compiler-errors
Copy link
Member

@compiler-errors compiler-errors commented Aug 26, 2024

Implement return type notation (RTN) in path position for where clauses. We already had RTN in associated type position (e.g.), but per the RFC:

As a standalone type, RTN can only be used as the Self type of a where-clause [...]

Specifically, in order to enable code like:

trait Foo {
    fn bar() -> impl Sized;
}

fn is_send(_: impl Send) {}

fn test<T>()
where
    T: Foo,
    T::bar(..): Send,
{
    is_send(T::bar());
}
  • In the resolver, when we see a TyKind::Path whose final segment is GenericArgs::ParenthesizedElided (i.e. (..)), resolve that path in the value namespace, since we're looking for a method.
  • When lowering where clauses in HIR lowering, we first try to intercept an RTN self type via lower_ty_maybe_return_type_notation. If we find an RTN type, we lower it manually in a way that respects its higher-ranked-ness (see below) and resolves to the corresponding RPITIT. Anywhere else, we'll emit the same "return type notation not allowed in this position yet" error we do when writing RTN in every other position.
  • In resolve_bound_vars, we add some special treatment for RTN types in where clauses. Specifically, we need to add new lifetime variables to our binders for the early- and late-bound vars we encounter on the method. This implements the higher-ranked desugaring laid out in the RFC.

This PR also adds a bunch of tests, mostly negative ones (testing error messages).

In a follow-up PR, I'm going to mark RTN as no longer incomplete, since this PR basically finishes the impl surface that we should initially stabilize, and the RFC was accepted.

cc RFC 3654 and #109417

@rustbot
Copy link
Collaborator

rustbot commented Aug 26, 2024

r? @jackh726

rustbot has assigned @jackh726.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Aug 26, 2024
@rustbot
Copy link
Collaborator

rustbot commented Aug 26, 2024

HIR ty lowering was modified

cc @fmease

@compiler-errors
Copy link
Member Author

Hi @jackh726. It's been 3 weeks since this was opened, so I'm pinging just to ask whether you're willing and free to review this PR, or if I should try to find someone else to review it.

@jackh726
Copy link
Member

Sorry! Yes, I'll take a look sometime this week.

Copy link
Member

@jackh726 jackh726 left a comment

Choose a reason for hiding this comment

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

I think I probably want to do a second pass over this, but I do think some more tests here are needed.

Comment on lines 1859 to 1862
// and a bound that looks like:
// `for<'a, 'b> <T as Trait<'a>>::x(): Other<'b>`
// this is going to expand to something like:
// `for<'a, 'b, 'r, T> <T as Trait<'a>>::x::<'r, T>::{opaque#0}: Other<'b>`.
Copy link
Member

Choose a reason for hiding this comment

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

Are there actually tests for this? I see one test to deny type parameters, but not for lifetimes.

Copy link
Member Author

Choose a reason for hiding this comment

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

This comment is a bit out of date. We don't support type params anyways, but tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.rs exercises when we have bound vars in both the trait ref, the method, and the goal.

Comment on lines +793 to +795
// If we have a path that ends with `(..)`, then it must be
// return type notation. Resolve that path in the *value*
// namespace.
Copy link
Member

Choose a reason for hiding this comment

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

This is fairly fragile, right? If, for example, the user write method(), we won't resolve things correctly.

Copy link
Member

Choose a reason for hiding this comment

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

At the very least, I would expect a test for this.

Copy link
Member Author

Choose a reason for hiding this comment

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

What specifically would you like tested here? When there's a conflicting definition in the type namespace?

Copy link
Member Author

Choose a reason for hiding this comment

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

I've added tests/ui/associated-type-bounds/return-type-notation/namespace-conflict.rs to ensure that we're not resolving to something in the type namespace (namely, a GAT).

IDK about method() by itself, but that needs to be resolved in the type namespace for bare Fn() trait objects. If you meant T::method() or <T as Trait>::method(): the diagnostics for the former (which we lower as a type-dependent path in HIR) are nice, but the latter (which we lower in resolve) are not. I can fix that as a follow-up, I guess :)

Copy link
Member

Choose a reason for hiding this comment

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

Can you add a test for what diagnostic we emit if the user writes method(): Send?

Copy link
Member Author

Choose a reason for hiding this comment

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

Should be covered by tests/ui/associated-type-bounds/return-type-notation/bad-inputs-and-output.rs and tests/ui/associated-type-bounds/return-type-notation/not-a-method.rs. The diagnostics could definitely use work on the resolver side, but I'll defer that to a follow-up.

Comment on lines +707 to +708
// Next, we need to check that the return-type notation is being used on
// an RPITIT (return-position impl trait in trait) or AFIT (async fn in trait).
Copy link
Member

Choose a reason for hiding this comment

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

This is...not a restriction I expect given the RFC:

The primary use case is to add bounds such as Send to the futures returned by async fns in traits and -> impl Future functions, but they work for any trait function defined with return-position impl trait (e.g., where T: Factory<widgets(..): DoubleEndedIterator> would also be valid).

At the very least, this should have a test.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

I've extended tests/ui/associated-type-bounds/return-type-notation/non-rpitit.rs to exercise this codepath, which was already exercised for the associated-type-bound flavor of RTN.

Copy link
Member

Choose a reason for hiding this comment

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

This is a restriction specifically called out in the RFC: https://rust-lang.github.io/rfcs/3654-return-type-notation.html#rtn-only-applies-to-afit-and-rpitit-methods

Hmm, that quoted text is from the RFC, the summary section. That should probably be amended.

Copy link
Member

@jackh726 jackh726 left a comment

Choose a reason for hiding this comment

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

Just the one comment remaining about adding a test for the mistyped method(): Send diagnostic, then r=me.

@@ -0,0 +1,49 @@
error[E0575]: expected function, found function `function`
Copy link
Member Author

Choose a reason for hiding this comment

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

Namely, this is a consequence of E0575 calling associated functions "functions".

As a follow-up, I will do like #130414 and probably split these resolver errors into their own error codes, so they can have tailored explanations.

@compiler-errors
Copy link
Member Author

@bors r=jackh726

@bors
Copy link
Contributor

bors commented Sep 21, 2024

📌 Commit 1de894f has been approved by jackh726

It is now in the queue for this repository.

@bors bors removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 21, 2024
@bors bors added the S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. label Sep 21, 2024
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Sep 21, 2024
…kh726

Implement Return Type Notation (RTN)'s path form in where clauses

Implement return type notation (RTN) in path position for where clauses. We already had RTN in associated type position ([e.g.](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=627a4fb8e2cb334863fbd08ed3722c09)), but per [the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#where-rtn-can-be-used-for-now):

> As a standalone type, RTN can only be used as the Self type of a where-clause [...]

Specifically, in order to enable code like:

```rust
trait Foo {
    fn bar() -> impl Sized;
}

fn is_send(_: impl Send) {}

fn test<T>()
where
    T: Foo,
    T::bar(..): Send,
{
    is_send(T::bar());
}
```

* In the resolver, when we see a `TyKind::Path` whose final segment is `GenericArgs::ParenthesizedElided` (i.e. `(..)`), resolve that path in the *value* namespace, since we're looking for a method.
* When lowering where clauses in HIR lowering, we first try to intercept an RTN self type via `lower_ty_maybe_return_type_notation`. If we find an RTN type, we lower it manually in a way that respects its higher-ranked-ness (see below) and resolves to the corresponding RPITIT. Anywhere else, we'll emit the same "return type notation not allowed in this position yet" error we do when writing RTN in every other position.
* In `resolve_bound_vars`, we add some special treatment for RTN types in where clauses. Specifically, we need to add new lifetime variables to our binders for the early- and late-bound vars we encounter on the method. This implements the higher-ranked desugaring [laid out in the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#converting-to-higher-ranked-trait-bounds).

This PR also adds a bunch of tests, mostly negative ones (testing error messages).

In a follow-up PR, I'm going to mark RTN as no longer incomplete, since this PR basically finishes the impl surface that we should initially stabilize, and the RFC was accepted.

cc [RFC 3654](rust-lang/rfcs#3654) and rust-lang#109417
bors added a commit to rust-lang-ci/rust that referenced this pull request Sep 21, 2024
…iaskrgr

Rollup of 9 pull requests

Successful merges:

 - rust-lang#127766 (add `extern "C-cmse-nonsecure-entry" fn` )
 - rust-lang#129629 (Implement Return Type Notation (RTN)'s path form in where clauses)
 - rust-lang#130246 (rustc_expand: remember module `#[path]`s during expansion)
 - rust-lang#130408 (Avoid re-validating UTF-8 in `FromUtf8Error::into_utf8_lossy`)
 - rust-lang#130651 (Add --enable-profiler to armhf dist)
 - rust-lang#130653 (ABI compatibility: mention Result guarantee)
 - rust-lang#130665 (Prevent Deduplication of `LongRunningWarn`)
 - rust-lang#130666 (Assert that `explicit_super_predicates_of` and `explicit_item_super_predicates` truly only contains bounds for the type itself)
 - rust-lang#130667 (compiler: Accept "improper" ctypes in extern "rust-cold" fn)

r? `@ghost`
`@rustbot` modify labels: rollup
bors added a commit to rust-lang-ci/rust that referenced this pull request Sep 21, 2024
…mpiler-errors

Rollup of 8 pull requests

Successful merges:

 - rust-lang#127766 (add `extern "C-cmse-nonsecure-entry" fn` )
 - rust-lang#129629 (Implement Return Type Notation (RTN)'s path form in where clauses)
 - rust-lang#130408 (Avoid re-validating UTF-8 in `FromUtf8Error::into_utf8_lossy`)
 - rust-lang#130651 (Add --enable-profiler to armhf dist)
 - rust-lang#130653 (ABI compatibility: mention Result guarantee)
 - rust-lang#130666 (Assert that `explicit_super_predicates_of` and `explicit_item_super_predicates` truly only contains bounds for the type itself)
 - rust-lang#130667 (compiler: Accept "improper" ctypes in extern "rust-cold" fn)
 - rust-lang#130673 (Parser: recover from `:::` to `::`)

r? `@ghost`
`@rustbot` modify labels: rollup
bors added a commit to rust-lang-ci/rust that referenced this pull request Sep 21, 2024
…mpiler-errors

Rollup of 8 pull requests

Successful merges:

 - rust-lang#127766 (add `extern "C-cmse-nonsecure-entry" fn` )
 - rust-lang#129629 (Implement Return Type Notation (RTN)'s path form in where clauses)
 - rust-lang#130408 (Avoid re-validating UTF-8 in `FromUtf8Error::into_utf8_lossy`)
 - rust-lang#130651 (Add --enable-profiler to armhf dist)
 - rust-lang#130653 (ABI compatibility: mention Result guarantee)
 - rust-lang#130666 (Assert that `explicit_super_predicates_of` and `explicit_item_super_predicates` truly only contains bounds for the type itself)
 - rust-lang#130667 (compiler: Accept "improper" ctypes in extern "rust-cold" fn)
 - rust-lang#130673 (Parser: recover from `:::` to `::`)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit d72d44d into rust-lang:master Sep 22, 2024
6 checks passed
@rustbot rustbot added this to the 1.83.0 milestone Sep 22, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Sep 22, 2024
Rollup merge of rust-lang#129629 - compiler-errors:rtn-in-path, r=jackh726

Implement Return Type Notation (RTN)'s path form in where clauses

Implement return type notation (RTN) in path position for where clauses. We already had RTN in associated type position ([e.g.](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=627a4fb8e2cb334863fbd08ed3722c09)), but per [the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#where-rtn-can-be-used-for-now):

> As a standalone type, RTN can only be used as the Self type of a where-clause [...]

Specifically, in order to enable code like:

```rust
trait Foo {
    fn bar() -> impl Sized;
}

fn is_send(_: impl Send) {}

fn test<T>()
where
    T: Foo,
    T::bar(..): Send,
{
    is_send(T::bar());
}
```

* In the resolver, when we see a `TyKind::Path` whose final segment is `GenericArgs::ParenthesizedElided` (i.e. `(..)`), resolve that path in the *value* namespace, since we're looking for a method.
* When lowering where clauses in HIR lowering, we first try to intercept an RTN self type via `lower_ty_maybe_return_type_notation`. If we find an RTN type, we lower it manually in a way that respects its higher-ranked-ness (see below) and resolves to the corresponding RPITIT. Anywhere else, we'll emit the same "return type notation not allowed in this position yet" error we do when writing RTN in every other position.
* In `resolve_bound_vars`, we add some special treatment for RTN types in where clauses. Specifically, we need to add new lifetime variables to our binders for the early- and late-bound vars we encounter on the method. This implements the higher-ranked desugaring [laid out in the RFC](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#converting-to-higher-ranked-trait-bounds).

This PR also adds a bunch of tests, mostly negative ones (testing error messages).

In a follow-up PR, I'm going to mark RTN as no longer incomplete, since this PR basically finishes the impl surface that we should initially stabilize, and the RFC was accepted.

cc [RFC 3654](rust-lang/rfcs#3654) and rust-lang#109417
@traviscross traviscross added the F-return_type_notation `#[feature(return_type_notation)]` label Nov 6, 2024
GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this pull request Nov 7, 2024
…=pnkfelix

Robustify and genericize return-type-notation resolution in `resolve_bound_vars`

rust-lang#129629 implemented return-type-notation (RTN) in its path form, like `where T::method(..): Bound`. As part of lowering, we must record the late-bound vars for the where clause introduced by the method (namely, its early- and late-bound lifetime arguments, since `where T::method(..)` turns into a higher-ranked where clause over all of the lifetimes according to [RFC 3654](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#converting-to-higher-ranked-trait-bounds)).

However, this logic was only looking at the where clauses of the parent item that the `T::method(..)` bound was written on, and not any parent items. This PR generalizes that logic to look at the parent item (i.e. the outer impl or trait) instead and fixes a (debug only) assertion as an effect.

This logic is also more general and likely easier to adapt to more interesting (though likely very far off) cases like non-lifetime binder `for<T: Trait> T::method(..): Send` bounds.

Tracking:

- rust-lang#109417
bors added a commit to rust-lang-ci/rust that referenced this pull request Nov 28, 2024
…jgillot

Robustify and genericize return-type-notation resolution in `resolve_bound_vars`

rust-lang#129629 implemented return-type-notation (RTN) in its path form, like `where T::method(..): Bound`. As part of lowering, we must record the late-bound vars for the where clause introduced by the method (namely, its early- and late-bound lifetime arguments, since `where T::method(..)` turns into a higher-ranked where clause over all of the lifetimes according to [RFC 3654](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#converting-to-higher-ranked-trait-bounds)).

However, this logic was only looking at the where clauses of the parent item that the `T::method(..)` bound was written on, and not any parent items. This PR generalizes that logic to look at the parent item (i.e. the outer impl or trait) instead and fixes a (debug only) assertion as an effect.

This logic is also more general and likely easier to adapt to more interesting (though likely very far off) cases like non-lifetime binder `for<T: Trait> T::method(..): Send` bounds.

Tracking:

- rust-lang#109417
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Dec 1, 2024
…=cjgillot

Robustify and genericize return-type-notation resolution in `resolve_bound_vars`

rust-lang#129629 implemented return-type-notation (RTN) in its path form, like `where T::method(..): Bound`. As part of lowering, we must record the late-bound vars for the where clause introduced by the method (namely, its early- and late-bound lifetime arguments, since `where T::method(..)` turns into a higher-ranked where clause over all of the lifetimes according to [RFC 3654](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#converting-to-higher-ranked-trait-bounds)).

However, this logic was only looking at the where clauses of the parent item that the `T::method(..)` bound was written on, and not any parent items. This PR generalizes that logic to look at the parent item (i.e. the outer impl or trait) instead and fixes a (debug only) assertion as an effect.

This logic is also more general and likely easier to adapt to more interesting (though likely very far off) cases like non-lifetime binder `for<T: Trait> T::method(..): Send` bounds.

Tracking:

- rust-lang#109417
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Dec 1, 2024
Rollup merge of rust-lang#132047 - compiler-errors:rbv-rtn-cleanup, r=cjgillot

Robustify and genericize return-type-notation resolution in `resolve_bound_vars`

rust-lang#129629 implemented return-type-notation (RTN) in its path form, like `where T::method(..): Bound`. As part of lowering, we must record the late-bound vars for the where clause introduced by the method (namely, its early- and late-bound lifetime arguments, since `where T::method(..)` turns into a higher-ranked where clause over all of the lifetimes according to [RFC 3654](https://rust-lang.github.io/rfcs/3654-return-type-notation.html#converting-to-higher-ranked-trait-bounds)).

However, this logic was only looking at the where clauses of the parent item that the `T::method(..)` bound was written on, and not any parent items. This PR generalizes that logic to look at the parent item (i.e. the outer impl or trait) instead and fixes a (debug only) assertion as an effect.

This logic is also more general and likely easier to adapt to more interesting (though likely very far off) cases like non-lifetime binder `for<T: Trait> T::method(..): Send` bounds.

Tracking:

- rust-lang#109417
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
F-return_type_notation `#[feature(return_type_notation)]` S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants