diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 101fcf880..41e16c5e6 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -113,7 +113,9 @@ - [Bound vars and Parameters](./bound-vars-and-params.md) - [Type inference](./type-inference.md) - [Trait solving](./traits/resolution.md) - - [Early and Late Bound Parameter Definitions](./early-late-bound.md) + - [Early and Late Bound Parameter Definitions](./early-late-bound-summary.md) + - [What are early and late bound parameters](./what-does-early-late-bound-mean.md) + - [Interactions with turbofishing](./turbofishing-and-early-late-bound.md) - [Higher-ranked trait bounds](./traits/hrtb.md) - [Caching subtleties](./traits/caching.md) - [Specialization](./traits/specialization.md) diff --git a/src/early-late-bound-summary.md b/src/early-late-bound-summary.md new file mode 100644 index 000000000..223e2251d --- /dev/null +++ b/src/early-late-bound-summary.md @@ -0,0 +1,10 @@ +# Early/Late bound parameters + +This section discusses what it means for generic parameters to be early or late bound. + +```rust +fn foo<'a, T>(b: &'a u32) -> &'a u32 { a } +// ^^ ^early bound +// ^^ +// ^^late bound +``` \ No newline at end of file diff --git a/src/turbofishing-and-early-late-bound.md b/src/turbofishing-and-early-late-bound.md new file mode 100644 index 000000000..a1d8a64e8 --- /dev/null +++ b/src/turbofishing-and-early-late-bound.md @@ -0,0 +1,120 @@ +# Turbofishing's interactions with early/late bound parameters + +The early/late bound parameter distinction on functions introduces some complications +when providing generic arguments to functions. This document discusses what those are +and how they might interact with future changes to make more things late bound. + +## Can't turbofish generic arguments on functions sometimes + +When a function has any late bound lifetime parameters (be they explicitly defined or +implicitly introduced via lifetime elision) we disallow specifying any lifetime arguments +on the function. Sometimes this is a hard error other times it is a future compat lint +([`late_bound_lifetime_arguments`](https://github.com/rust-lang/rust/issues/42868)). + +```rust +fn early<'a: 'a>(a: &'a ()) -> &'a () { a } +fn late<'a>(a: &'a ()) -> &'a () { a } + +fn mixed<'a, 'b: 'b>(a: &'a (), b: &'b ()) -> &'a () { a } + +struct Foo; +impl Foo { + fn late<'a>(self, a: &'a ()) -> &'a () { a } +} + +fn main() { + // fine + let f = early::<'static>; + + // some variation of hard errors and future compat lints + Foo.late::<'static>(&()); + let f = late::<'static>; + let f = mixed::<'static, 'static>; + let f = mixed::<'static>; + late::<'static>(&()); +} +``` + +The justification for this is that late bound parameters are not present on the +`FnDef` so the arguments to late bound parameters can't be present in the substs +for the type. i.e. the `late` function in the above code snippet would not have +any generic parameters on the `FnDef` zst: +```rust +// example desugaring of the `late` function and its zst + builtin Fn impl +struct LateFnDef; +impl<'a> Fn<(&'a ())> for LateFnDef { + type Output = &'a (); + ... +} +``` + +The cause for some situations giving future compat lints and others giving hard errors +is a little arbitrary but explainable: +- It's always a hard error for method calls +- It's only a hard error on paths to free functions if there is no unambiguous way to +create the substs for the fndef from the lifetime arguments. (i.e. the amount of +lifetimes provided must be exactly equal to the amount of early bound lifetimes or +else it's a hard error) + +## Back compat issues from turning early bound to late bound + +Because of the previously mentioned restriction on turbofishing generic arguments, it +is a breaking change to upgrade a lifetime from early bound to late bound as it can cause +existing turbofishies to become hard errors/future compat lints. + +Many t-types members have expressed interest in wanting more parameters to be late bound. +We cannot do so if making something late bound is going to break code that many would +expect to work (judging by the future compat lint issue many people do expect to be able +to turbofish late bound parameters). + +## Interactions with late bound type/const parameters + +If we were to make some type/const parameters late bound we would definitely not want +to disallow turbofishing them as it presumably(?) would break a Tonne of code. + +While lifetimes do differ from type/consts in some ways I(BoxyUwU) do not believe there +is any justification for why it would make sense to allow turbofishing late bound +type/const parameters but not late bound lifetimes. + +## Removing the hard error/fcw + +From reasons above it seems reasonable that we may want to remove the hard error and fcw +(removing the errors/fcw is definitely a blocker for making more things late bound). + +example behaviour: +```rust +fn late<'a>(a: &'a ()) -> &'a () { a } + +fn accepts_fn(_: impl for<'a> Fn(&'a ()) -> &'a ()) {} +fn accepts_fn_2(_: impl Fn(&'static ()) -> &'static ()) {} + +fn main() { + let f = late::<'static>; + + accepts_fn(f); //~ error: `f` doesnt implement `for<'a> Fn(&'a ()) -> &'a ()` + accepts_fn_2(f) // works + + accepts_fn(late) // works +} +```` + +one potential complication is that we would want a way to specify a generic argument +to a function without having to specify arguments for all previous parameters. i.e. +ideally you could write the following code somehow. +```rust +fn late<'a, 'b>(_: &'a (), _: &'b ()) {} + +fn accepts_fn(_: impl for<'a> Fn(&'a (), &'static ())) {} + +fn main() { + // a naive implementation would have a `ReInfer` as the subst for `'a` parameter + // no longer allowing the FnDef to satisfy the `for<'a> Fn(&'a ()` bound + let f = late::<'_, 'static>; + accepts_fn(f); +} +``` +Maybe we can just special case astconv for `_`/`'_` arguments for late bound parameters somehow +and have it not mean the same thing as `_` for early bound parameters. Regardless I think we +would need a solution that would allow writing the above code even if it was done by some new +syntax such as havign to write `late::` (naturally `k#no_argument` +would only make sense as an argument to late bound parameters). diff --git a/src/early-late-bound.md b/src/what-does-early-late-bound-mean.md similarity index 100% rename from src/early-late-bound.md rename to src/what-does-early-late-bound-mean.md