Repository: rust
Issue: rustdoc: Consider eliminating edition sensitivity around RPIT capturing
Labels: T-rustdoc,A-impl-trait,C-discussion

Uplifted from https://github.com/rust-lang/rust/issues/127228#issuecomment-2201462571 and [#t-lang/blog post about precise capture (comment)](https://rust-lang.zulipchat.com/#narrow/channel/213817-t-lang/topic/blog.20post.20about.20precise.20capture/near/477498874).

---

At the time of writing, rustdoc renders RPITs (return-position impl-Trait) basically identical to how they appear in the source code, most important of which is impl-Trait captures (which boils down to the presence, absence and contents of `use<…>` bounds for all practical purposes). Since RPIT (but not RPITIT) capturing is edition sensitive, it follows that rustdoc's RPIT rendering is edition sensitive as well. This leads us to the point of discussion: Should we (somehow) eliminate this edition sensitivity in rustdoc?

Consider the following signature as it might be shown by rustdoc: `pub fn f(x: &()) -> impl Sized`. Unless you know the edition of the containing crate (which could be regarded as an implementation detail?), you won't know if the RPIT `impl Sized` captures the anonymous lifetime of the parameter (i.e., `impl Sized + use<'_>`) or not (i.e., `impl Sized + use<>`). I believe this to be less than ideal

Edition insensitivity would solve this:

**(A)** The solution I initially thought of would comprise rendering synthetic `use<…>` bounds on `use<…>`-less[^1] RPITs *whenever* we have *any*[^2] generic parameters in *all* editions! Note: If we have *anonymous* lifetime parameters, we can't always easily synthesize a `use<…>` bound[^3].

However, this leads to more verbose output and fixates too much on pre-2024 edition behavior. Assuming the 2021 edition and before lose relevance by the minute, this solution is stuck in the past. Moreover, <2024 RPIT capturing behavior is the odd one out among RPITIT, TAIT, ATPIT (and ≥2024 RPIT).

**(B)** I guess the more realistic and forward-thinking solution would be to render ≥2024 RPITs as is and to *always* render a synthetic `use<…>` bounds on `use<…>`-less[^1] <2024 RPITs if there are *any*[^2] generic parameters in scope. We don't face the "anon lifetime" issues solution (A) has because the synthetic `use<…>` bounds should *never* (need to) contain any lifetime parameters! In fact, if we didn't care about non-lifetime generic parameters, we could get away with just using `+ use<>` for ≤2021 edition crates.

---

I'm pretty sure this whole discussion is also relevant to rustdoc JSON (does its output even contain the edition?).

[^1]: Natural `use<…>` bounds are already edition agnostic/insensitive.
[^2]: I'm aware that opaque types are currently required to capture all in-scope type and const parameters (which is a *temporary* restriction I assume), so we could relax the rule to only consider lifetime parameters.
[^3]: Consider this Rust 2024(!) snippet: `pub fn f(x: &(), y: &()) -> impl Sized`. The opaque type `impl Sized` captures *both* anonymous/elided lifetime parameters. We can't denote this, `use<'_>` is not *really* an option since it's semantically ill-formed. I guess we could still use it as a sort of pseudo-Rust (rustdoc already employs pseudo-Rust syntax in certain cases). Otherwise, we could render it as `pub fn f<'a, 'b>(x: &'a (), y: &'b ()) -> impl Sized + use<'a, 'b>` (ensuring that `'a` and `'b` occur *free* in the signature) or pseudo-Rust `pub fn f(x: &'0 (), y: &'1 ()) -> impl Sized + use<'0, '1>`.