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

Stabilize #![feature(precise_capturing_in_traits)] #138128

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

compiler-errors
Copy link
Member

@compiler-errors compiler-errors commented Mar 6, 2025

Precise capturing (+ use<> bounds) in traits - Stabilization Report

Fixes #130044.

Stabilization summary

This report proposes the stabilization of use<> precise capturing bounds in return-position impl traits in traits (RPITITs). This completes a missing part of [RFC 3617 "Precise capturing"].

Precise capturing in traits was not ready for stabilization when the first subset was proposed for stabilization (namely, RPITs on free and inherent functions - #127672) since this feature has a slightly different implementation, and it hadn't yet been implemented or tested at the time. It is now complete, and the type system implications of this stabilization are detailed below.

Motivation

Currently, RPITITs capture all in-scope lifetimes, according to the decision made in the "lifetime capture rules 2024" RFC. However, traits can be designed such that some lifetimes in arguments may not want to be captured. There is currently no way to express this.

Major design decisions since the RFC

No major decisions were made. This is simply an extension to the RFC that was understood as a follow-up from the original stabilization.

What is stabilized?

Users may write + use<'a, T> bounds on their RPITITs. This conceptually modifies the desugaring of the RPITIT to omit the lifetimes that we would copy over from the method. For example,

trait Foo {
    fn method<'a>(&'a self) -> impl Sized;
    
    // ... desugars to something like:
    type RPITIT_1<'a>: Sized;
    fn method_desugared<'a>(&'a self) -> Self::RPITIT_1<'a>;
    
    // ... whereas with precise capturing ...
    fn precise<'a>(&'a self) -> impl Sized + use<Self>;
    
    // ... desugars to something like:
    type RPITIT_2: Sized;
    fn precise_desugared<'a>(&'a self) -> Self::RPITIT_2;
}

And thus the GAT doesn't name 'a. In the compiler internals, it's not implemented exactly like this, but not in a way that users should expect to be able to observe.

Limitations on what generics must be captured

Currently, we require that all generics from the trait (including the Self) type are captured. This is because the generics from the trait are required to be invariant in order to do associated type normalization.

And like regular precise capturing bounds, all type and const generics in scope must be captured.

Thus, only the in-scope method lifetimes may be relaxed with this syntax today.

What isn't stabilized? (a.k.a. potential future work)

See section above. Relaxing the requirement to capture all type and const generics in scope may be relaxed when #130043 is implemented, however it currently interacts with some underexplored corners of the type system (e.g. unconstrained type bivariance) so I don't expect it to come soon after.

Implementation summary

This functionality is implemented analogously to the way that opaque type precise capturing works.

Namely, we currently use variance to model the capturedness of lifetimes. However, since RPITITs are anonymous GATs instead of opaque types, we instead modify the type relation of GATs to consider variances for RPITITs (along with opaque types which it has done since #103491).

// Computes the variances for an alias (opaque or RPITIT) that represent
// its (un)captured regions.
pub fn opt_alias_variances(
self,
kind: impl Into<ty::AliasTermKind>,
def_id: DefId,
) -> Option<&'tcx [ty::Variance]> {
match kind.into() {
ty::AliasTermKind::ProjectionTy => {
if self.is_impl_trait_in_trait(def_id) {
Some(self.variances_of(def_id))
} else {
None
}
}
ty::AliasTermKind::OpaqueTy => Some(self.variances_of(def_id)),
ty::AliasTermKind::InherentTy
| ty::AliasTermKind::WeakTy
| ty::AliasTermKind::UnevaluatedConst
| ty::AliasTermKind::ProjectionConst => None,
}
}
}

let args = if let Some(variances) = cx.opt_alias_variances(a.kind(cx), a.def_id) {
relate_args_with_variances(
relation, a.def_id, variances, a.args, b.args,
false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
)?

Using variance to model capturedness is an implementation detail, and in the future it would be desirable if opaques and RPITITs simply did not include the uncaptured lifetimes in their generics. This can be changed in a forwards-compatible way, and almost certainly would not be observable by users (at least not negatively, since it may indeed fix some bugs along the way).

Tests

  • Test that the lifetime isn't actually captured: tests/ui/impl-trait/precise-capturing/rpitit.rs and tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs and tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs.
  • Technical test for variance computation: tests/ui/impl-trait/in-trait/variance.rs.
  • Test that you must capture all trait generics: tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs.
  • Test that you cannot capture more than what the trait specifies: tests/ui/impl-trait/precise-capturing/rpitit-captures-more-method-lifetimes.rs and tests/ui/impl-trait/precise-capturing/rpitit-impl-captures-too-much.rs.
  • Undercapturing (refinement) lint: tests/ui/impl-trait/in-trait/refine-captures.rs.

What other unstable features may be exposed by this feature?

I don't believe that this exposes any new unstable features indirectly.

Remaining bugs and open issues

Not aware of any open issues or bugs.

Tooling support

Rustfmt: ✅ Supports formatting + use<> everywhere.

Clippy: ✅ No support needed, unless specific clippy lints are impl'd to care for precise capturing itself.

Rustdoc: ✅ Rendering + use<> precise capturing bounds is supported.

Rust-analyzer: ✅ Parser support, and then lifetime support isn't needed #138128 (comment) (previous: ❓ There is parser support, but I am unsure of rust-analyzer's level of support for RPITITs in general.)

History

Tracking issue: #130044

@rustbot
Copy link
Collaborator

rustbot commented Mar 6, 2025

r? @SparrowLii

rustbot has assigned @SparrowLii.
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 Mar 6, 2025
@compiler-errors compiler-errors added T-lang Relevant to the language team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 6, 2025
@compiler-errors
Copy link
Member Author

I'm putting up this staiblization report up with a request for comments. I think it's basiclaly as ready as it's going to get, but it's very likely I'm missing something that someone wants to see here 😸

cc @rust-lang/lang and @rust-lang/types -- would be cool if y'all could point out anything you'd like to see that's missing from this doc.

@compiler-errors compiler-errors added S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 6, 2025
@traviscross
Copy link
Contributor

Fantastic. Huge thanks to @compiler-errors for... let me count the ways:

  • Putting together the original experimental implementation of RFC 3617 quickly enough that it could be used in writing and checking the correctness of that RFC.
  • Getting the implementation of the original part polished, tested, and put together so as to unblock the stabilization not just of use<..> precise capturing, but through that, the stabilization of the RFC 3498 "Lifetime Capture Rules 2024", a lang priority item, and through that, the timely and complete release of the Rust 2024 edition.
  • Being a great partner and an enormous help in all ways in writing both RFCs and working through the process of getting them accepted.
  • And now, in following up on this to implement and stabilize more of RFC 3617, increasing the expressiveness and utility of Rust.

Having reviewed the (exemplary) stabilization report and all tests, and having beat on this a bit myself, this all looks right to me, so I propose:

@rfcbot fcp merge
@rustbot labels +I-lang-nominated

@rfcbot
Copy link

rfcbot commented Mar 6, 2025

Team member @traviscross has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns.
See this document for info about what commands tagged team members can give me.

@rustbot rustbot added the I-lang-nominated Nominated for discussion during a lang team meeting. label Mar 6, 2025
@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Mar 6, 2025
@compiler-errors
Copy link
Member Author

compiler-errors commented Mar 6, 2025

This is blocked on tool support OR deciding it can be done later (not particularly married to either option)

@traviscross
Copy link
Contributor

traviscross commented Mar 6, 2025

cc @Veykril @rust-lang/rust-analyzer

@traviscross traviscross assigned oli-obk and unassigned SparrowLii Mar 6, 2025
@ChayimFriedman2
Copy link
Contributor

ChayimFriedman2 commented Mar 7, 2025

@traviscross AFAIK we (rust-analyzer) don't model capturing yet (our lifetime handling is very-incomplete-to-outright-missing), and goto def etc. should already work.

@nikomatsakis
Copy link
Contributor

@rfcbot reviewed

1 similar comment
@tmandry
Copy link
Member

tmandry commented Mar 12, 2025

@rfcbot reviewed

@traviscross
Copy link
Contributor

@rustbot labels -I-lang-nominated +I-lang-radar

We discussed this in the lang call today to resounding applause.

@rustbot rustbot added I-lang-radar Items that are on lang's radar and will need eventual work or consideration. and removed I-lang-nominated Nominated for discussion during a lang team meeting. labels Mar 12, 2025
@rfcbot rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels Mar 13, 2025
@rfcbot
Copy link

rfcbot commented Mar 13, 2025

🔔 This is now entering its final comment period, as per the review above. 🔔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). T-lang Relevant to the language team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Tracking issue for RFC 3617 precise capturing in traits
9 participants