Stream: t-compiler

Topic: Cleaning up async/await


Taylor Cramer (Jan 10 2019 at 18:07, on Zulip):

@nikomatsakis do you have a preference for how to express "the minimum of all the following lifetimes" in HIR?

Taylor Cramer (Jan 10 2019 at 18:09, on Zulip):

I'm looking to clean up the async/await implementation and one of the big issues is that you can't have multiple different named lifetime parameters

Taylor Cramer (Jan 10 2019 at 18:10, on Zulip):

because the returned impl Future can live only as long as the shortest input lifetime

Taylor Cramer (Jan 10 2019 at 18:10, on Zulip):

and there's not a way to express that at the moment AFAICT

nikomatsakis (Jan 10 2019 at 21:51, on Zulip):

@Taylor Cramer well 'a and 'b...'z: 'a

nikomatsakis (Jan 10 2019 at 21:52, on Zulip):

is how you would express it

nikomatsakis (Jan 10 2019 at 21:53, on Zulip):

e.g.

fn foo<'a, 'b, 'c>()
where
  'b: 'a,
  'c: 'a,
{
}
nikomatsakis (Jan 10 2019 at 21:53, on Zulip):

not sure if that's what you want in HIR

Taylor Cramer (Jan 10 2019 at 22:02, on Zulip):

@nikomatsakis Yeah I wasn't sure if there was a better way than introducing a new unnameable lifetime everywhere

Taylor Cramer (Jan 10 2019 at 22:02, on Zulip):

but I can certainly do it that way

nikomatsakis (Jan 10 2019 at 22:26, on Zulip):

@Taylor Cramer can you say a bit more about what's causing the root error?

nikomatsakis (Jan 10 2019 at 22:27, on Zulip):

or maybe I just need to revisit this with a clear head

Taylor Cramer (Jan 10 2019 at 22:27, on Zulip):

@nikomatsakis today we don't allow async fn with multiple named lifetimes in the arguments

nikomatsakis (Jan 10 2019 at 22:27, on Zulip):

basically i'm wondering if the right path is instead to fix impl trait or something like that

Taylor Cramer (Jan 10 2019 at 22:27, on Zulip):

yeah maybe

Taylor Cramer (Jan 10 2019 at 22:27, on Zulip):

we do this because we need a lifetime to tack onto the impl Future<...> + 'something in the return type

Taylor Cramer (Jan 10 2019 at 22:28, on Zulip):

we also ban multiple elided lifetimes for the same reason

Taylor Cramer (Jan 10 2019 at 22:28, on Zulip):

and require that you name them

Taylor Cramer (Jan 10 2019 at 22:29, on Zulip):

So I'm looking to remove that restriction

Taylor Cramer (Jan 10 2019 at 22:30, on Zulip):

because aturon and boats thought it was a blocker for stabilization

Taylor Cramer (Jan 10 2019 at 22:30, on Zulip):

but I'm not quite of the best path towards a fix

Taylor Cramer (Jan 10 2019 at 22:41, on Zulip):

@nikomatsakis did you have some other thought about how we should be handling this?

nikomatsakis (Jan 11 2019 at 20:25, on Zulip):

@Taylor Cramer sorry I had to disappear and just got back to read

nikomatsakis (Jan 11 2019 at 20:25, on Zulip):

It would be great if you could give me a specific example

Taylor Cramer (Jan 11 2019 at 20:26, on Zulip):

sure!

nikomatsakis (Jan 11 2019 at 20:26, on Zulip):

in particular, I'm wondering:

Is this related to that problem of "hidden lifetimes" that we encontered with impl trait?

Taylor Cramer (Jan 11 2019 at 20:26, on Zulip):

Not particularly, no

nikomatsakis (Jan 11 2019 at 20:26, on Zulip):

ok

nikomatsakis (Jan 11 2019 at 20:26, on Zulip):

then an example would definitely be useful

Taylor Cramer (Jan 11 2019 at 20:27, on Zulip):

async fn foo<'a, 'b>(x: &'a u8, y: &'b u8) {}

Taylor Cramer (Jan 11 2019 at 20:27, on Zulip):

needs to desugar to something like

Taylor Cramer (Jan 11 2019 at 20:28, on Zulip):

fn foo<'a, 'b, 'c>(x: &'a u8, y: &'b u8) -> impl Future<Output = ()> + 'c where 'a: 'c, 'b: 'c { ... }

Taylor Cramer (Jan 11 2019 at 20:28, on Zulip):

or rather

Taylor Cramer (Jan 11 2019 at 20:28, on Zulip):

fn foo<'a, 'b>(x: &'a u8, y: &'b u8) -> impl Future<Output = ()> + 'intersection_of_a_and_b { ... }

nikomatsakis (Jan 11 2019 at 20:29, on Zulip):

it seems at least partly related

Taylor Cramer (Jan 11 2019 at 20:30, on Zulip):

"medium related"?

Taylor Cramer (Jan 11 2019 at 20:30, on Zulip):

it's a distinct issue

nikomatsakis (Jan 11 2019 at 20:30, on Zulip):

(I have to say I sometimes regret that we didn't adopt the "explicit captures in the impl" design)

nikomatsakis (Jan 11 2019 at 20:30, on Zulip):

well, is it?

nikomatsakis (Jan 11 2019 at 20:31, on Zulip):

/me thinks a bit

nikomatsakis (Jan 11 2019 at 20:32, on Zulip):

the intention is clearly that you should be able to "work with" data from x and y during the fn as normal

nikomatsakis (Jan 11 2019 at 20:33, on Zulip):

I think what i'm wondering about is basically "why do we need a 'c exactly"

nikomatsakis (Jan 11 2019 at 20:34, on Zulip):

if we had the impl<...> explicit syntax, you might have imagined desugaring to

fn foo<'a, 'b>(...) -> impl<'a, 'b>  Future<Output = ()>

syntax, right?

nikomatsakis (Jan 11 2019 at 20:35, on Zulip):

I'm trying to remember what our HIR setup is internally, wondering if we can desugar to something that doesn't have a direct syntax right now

Taylor Cramer (Jan 11 2019 at 20:44, on Zulip):

I don't believe there's a direct way to represent that in HIR today

Taylor Cramer (Jan 11 2019 at 20:44, on Zulip):

@nikomatsakis similarly, it's not just an issue with named lifetimes but also with elided ones

nikomatsakis (Jan 11 2019 at 20:45, on Zulip):

I think you can represent it

Taylor Cramer (Jan 11 2019 at 20:45, on Zulip):

oh?

nikomatsakis (Jan 11 2019 at 20:45, on Zulip):

put another way, this desugars into an existential type

nikomatsakis (Jan 11 2019 at 20:45, on Zulip):

(ultimately)

nikomatsakis (Jan 11 2019 at 20:45, on Zulip):
existential type Foo<'a, 'b>;

fn foo<'a, 'b>(...) -> Foo<'a, 'b>
nikomatsakis (Jan 11 2019 at 20:46, on Zulip):

I'm not sure how straightforward it is to set that stuff up, but I think that is what you want to express, isn't it?

Taylor Cramer (Jan 11 2019 at 20:46, on Zulip):

here's the code in question: https://github.com/rust-lang/rust/blob/master/src/librustc/hir/lowering.rs#L2086

Taylor Cramer (Jan 11 2019 at 20:47, on Zulip):

and it does this today by pushing a bound on: https://github.com/rust-lang/rust/blob/master/src/librustc/hir/lowering.rs#L2285

Taylor Cramer (Jan 11 2019 at 20:47, on Zulip):

but perhaps if we didn't use lower_existential_impl_trait and bypassed it

Taylor Cramer (Jan 11 2019 at 20:47, on Zulip):

https://github.com/rust-lang/rust/blob/master/src/librustc/hir/lowering.rs#L2236

Taylor Cramer (Jan 11 2019 at 20:49, on Zulip):

we could add more things to the lifetime_defs list https://github.com/rust-lang/rust/blob/master/src/librustc/hir/lowering.rs#L1345

Taylor Cramer (Jan 11 2019 at 20:49, on Zulip):

and we could omit that lifetime bound entirely

Taylor Cramer (Jan 11 2019 at 20:52, on Zulip):

Yeah, that fails on nightly today

Taylor Cramer (Jan 11 2019 at 20:53, on Zulip):

@nikomatsakis : https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9688e3897e0dddf9a7ddae5ab0c46ad8

Taylor Cramer (Jan 11 2019 at 20:53, on Zulip):

"ambiguous lifetime bound: neither 'a nor 'b outlives the other"

nikomatsakis (Jan 11 2019 at 20:56, on Zulip):

ah yeah we added that limitation

nikomatsakis (Jan 11 2019 at 20:56, on Zulip):

I've been thinking about how to lift it by making the region solver smarter

nikomatsakis (Jan 11 2019 at 20:56, on Zulip):

(for other reasons)

nikomatsakis (Jan 11 2019 at 20:56, on Zulip):

but I guess that's probably a bit off

nikomatsakis (Jan 11 2019 at 20:58, on Zulip):

well, I guess that adding an implicit bound is the best we can do today; I'd probably prefer that to making the HIR more magical.. btw how do we deal with "late-bound vs early-bound" today in this sort of thing, do you know? do we effectively make all regions in an async fn early bound?

Taylor Cramer (Jan 11 2019 at 21:49, on Zulip):

I believe the opposite

Taylor Cramer (Jan 11 2019 at 21:49, on Zulip):

if you try and apply any lifetimes

Taylor Cramer (Jan 11 2019 at 21:49, on Zulip):

(manually)

Taylor Cramer (Jan 11 2019 at 21:49, on Zulip):

it complains that you can't apply late-bound lifetimes

Taylor Cramer (Jan 11 2019 at 21:55, on Zulip):

@nikomatsakis So for now you'd recommend introducing a new unnameable (in the surface lang) lifetime that we can use for this purpose? And somehow specifying that '_ must outlive that lifetime?

Taylor Cramer (Jan 11 2019 at 21:56, on Zulip):

it all seems extremely dodgy to me but I don't have a good idea of how to do this in a more principled way

Taylor Cramer (Jan 16 2019 at 20:33, on Zulip):

Well, I guess I'm going to give this a shot b.c. after sleeping on it for a while I still have no idea how to do this in a more reasonable way

Taylor Cramer (Jan 16 2019 at 20:34, on Zulip):

so my plan is to expand all elided lifetimes in elidable input positions to separate named elided async lifetime #<..> lifetimes

Taylor Cramer (Jan 16 2019 at 20:34, on Zulip):

add them all to the parameters of the fn

Taylor Cramer (Jan 16 2019 at 20:37, on Zulip):

and add a lifetime named async (note the lack of a ' to prevent collision) and add a requirement to the where clause that all lifetimes that appear in the function signature that aren't introduced there (by e.g. for<..> or elided lifetimes inside fn/Fn syntax) must outlive the lifetime named async

Taylor Cramer (Jan 16 2019 at 20:37, on Zulip):

this seems likely to cause some messy code and some less-than-desirable diagnostics

Taylor Cramer (Jan 16 2019 at 20:37, on Zulip):

but It's all I've come up with at this point

Last update: Nov 16 2019 at 01:05UTC