-
Notifications
You must be signed in to change notification settings - Fork 12.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rollup merge of #100508 - BoxyUwU:make_less_things_late_bound, r=niko…
…matsakis avoid making substs of type aliases late bound when used as fn args fixes #47511 fixes #85533 (although I did not know theses issues existed when i was working on this 🙃) currently `Alias<...>` is treated the same as `Struct<...>` when deciding if generics should be late bound or early bound but this is not correct as `Alias` might normalize to a projection which does not constrain the generics. I think this needs more tests before merging more explanation of PR [here](https://hackmd.io/v44a-QVjTIqqhK9uretyQg?view) Hackmd inline for future readers: --- This assumes reader is familiar with the concept of early/late bound lifetimes. There's a section on rustc-dev-guide if not (although i think some details are a bit out of date) ## problem & background Not all lifetimes on a fn can be late bound: ```rust fn foo<'a>() -> &'a (); impl<'a> Fn<()> for FooFnDef { type Output = &'a (); // uh oh unconstrained lifetime } ``` so we make make them early bound ```rust fn foo<'a>() -> &'a (); impl<'a> Fn<()> for FooFnDef<'a> {// wow look at all that lifetimey type Output = &'a (); } ``` (Closures have the same constraint however it is not enforced leading to soundness bugs, [#84385](#84385) implements this "downgrading late bound to early bound" for closures) lifetimes on fn items are only late bound when they are "constrained" by the fn args: ```rust fn foo<'a>(_: &'a ()) -> &'a (); // late bound, not present on `FooFnItem` // vv impl<'a> Trait<(&'a (),)> for FooFnItem { type Output = &'a (); } // projections do not constrain inputs fn bar<'a, T: Trait>(_: <T as Trait<'a>>::Assoc) -> &'a (); // early bound // vv impl<'a, T: Trait> Fn<(<T as Trait<'a>>::Assoc,)> for BarFnItem<'a, T> { type Output = &'a (); } ``` current logic for determining if inputs "constrain" a lifetime works off of HIR so does not normalize aliases. It also assumes that any path with no self type constrains all its substs (i.e. `Foo<'a, u32>` has no self type but `T::Assoc` does). This falls apart for top level type aliases (see linked issues): ```rust type Alias<'a, T> = <T as Trait<'a>>::Assoc; // wow look its a path with no self type uwu // i bet that constrains `'a` so it should be latebound // vvvvvvvvvvv fn foo<'a, T: Trait>(_: Alias<'a, T>) -> &'a (); // `Alias` normalized to make things clearer // vvvvvvvvvvvvvvvvvvvvvvv impl<'a, T: Trait> Fn<(<T as Trait<'a>>::Assoc,)> for FooFnDef<T> { type Output = &'a (); // oh no `'a` isnt constrained wah wah waaaah *trumbone noises* // i think, idk what musical instrument that is } ``` ## solution The PR solves this by having the hir visitor that checks for lifetimes in constraining uses check if the path is a `DefKind::Alias`. If it is we ""normalize"" it by calling `type_of` and walking the returned type. This is a bit hacky as it requires a mapping between the substs on the path in hir, and the generics of the `type Alias<...>` which is on the ty layer. Alternative solutions may involve calculating the "late boundness" of lifetimes after/during astconv rather than relying on hir at all. We already have code to determine whether a lifetime SHOULD be late bound or not as this is currently how the error for `fn foo<'a, T: Trait>(_: Alias<'a, T>) -> &'a ();` gets emitted. It is probably not possible to do this right now, late boundness is used by `generics_of` and `gather_explicit_predicates_of` as we currently do not put late bound lifetimes in `Generics`. Although this seems sus to me as the long term goal is to make all generics late bound which would result in `generics_of(function)` being empty? [#103448](#103448) places all lifetimes in `Generics` regardless of late boundness so that may be a good step towards making this possible.
- Loading branch information
Showing
9 changed files
with
184 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
pub trait Trait<'a> { | ||
type Assoc; | ||
} | ||
|
||
pub type Alias<'a, T> = <T as Trait<'a>>::Assoc; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// aux-build:upstream_alias.rs | ||
// check-pass | ||
|
||
extern crate upstream_alias; | ||
|
||
fn foo<'a, T: for<'b> upstream_alias::Trait<'b>>(_: upstream_alias::Alias<'a, T>) -> &'a () { | ||
todo!() | ||
} | ||
|
||
fn main() {} |
24 changes: 24 additions & 0 deletions
24
src/test/ui/late-bound-lifetimes/downgraded_to_early_through_alias.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// check-pass | ||
|
||
trait Gats<'a> { | ||
type Assoc; | ||
type Assoc2; | ||
} | ||
|
||
trait Trait: for<'a> Gats<'a> { | ||
fn foo<'a>(_: &mut <Self as Gats<'a>>::Assoc) -> <Self as Gats<'a>>::Assoc2; | ||
} | ||
|
||
impl<'a> Gats<'a> for () { | ||
type Assoc = &'a u32; | ||
type Assoc2 = (); | ||
} | ||
|
||
type GatsAssoc<'a, T> = <T as Gats<'a>>::Assoc; | ||
type GatsAssoc2<'a, T> = <T as Gats<'a>>::Assoc2; | ||
|
||
impl Trait for () { | ||
fn foo<'a>(_: &mut GatsAssoc<'a, Self>) -> GatsAssoc2<'a, Self> {} | ||
} | ||
|
||
fn main() {} |
7 changes: 1 addition & 6 deletions
7
src/test/ui/issues/issue-47511.rs → ...st/ui/late-bound-lifetimes/issue-47511.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
src/test/ui/late-bound-lifetimes/late_bound_through_alias.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// check-pass | ||
|
||
fn f(_: X) -> X { | ||
unimplemented!() | ||
} | ||
|
||
fn g<'a>(_: X<'a>) -> X<'a> { | ||
unimplemented!() | ||
} | ||
|
||
type X<'a> = &'a (); | ||
|
||
fn main() { | ||
let _: for<'a> fn(X<'a>) -> X<'a> = g; | ||
let _: for<'a> fn(X<'a>) -> X<'a> = f; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// ensures that we don't ICE when there are too many args supplied to the alias. | ||
|
||
trait Trait<'a> { | ||
type Assoc; | ||
} | ||
|
||
type Alias<'a, T> = <T as Trait<'a>>::Assoc; | ||
|
||
fn bar<'a, T: Trait<'a>>(_: Alias<'a, 'a, T>) {} | ||
//~^ error: this type alias takes 1 lifetime argument but 2 lifetime arguments were supplied | ||
|
||
fn main() {} |
17 changes: 17 additions & 0 deletions
17
src/test/ui/late-bound-lifetimes/mismatched_arg_count.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
error[E0107]: this type alias takes 1 lifetime argument but 2 lifetime arguments were supplied | ||
--> $DIR/mismatched_arg_count.rs:9:29 | ||
| | ||
LL | fn bar<'a, T: Trait<'a>>(_: Alias<'a, 'a, T>) {} | ||
| ^^^^^ -- help: remove this lifetime argument | ||
| | | ||
| expected 1 lifetime argument | ||
| | ||
note: type alias defined here, with 1 lifetime parameter: `'a` | ||
--> $DIR/mismatched_arg_count.rs:7:6 | ||
| | ||
LL | type Alias<'a, T> = <T as Trait<'a>>::Assoc; | ||
| ^^^^^ -- | ||
|
||
error: aborting due to previous error | ||
|
||
For more information about this error, try `rustc --explain E0107`. |