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

Do not consider async task_context to be alive across yields #105668

Closed
wants to merge 1 commit into from

Conversation

Swatinem
Copy link
Contributor

@Swatinem Swatinem commented Dec 13, 2022

When lowering async constructs to generators, the resume argument is guaranteed not to be alive across yield points. However the simple generator_interior analysis thinks it is. Because of that, the resume ty was part of the GeneratorWitness and considered to be part of the generator type, even though it is not really.

This prevented async blocks from being UnwindSafe, and possibly Send in some cases.

The code now special cases the fact that the task_context of async blocks is never alive across yield points.

This fixes one regression from #105250.

r? @compiler-errors

@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 Dec 13, 2022
@Swatinem
Copy link
Contributor Author

@khuey would you please give this PR a try to see if it indeed fixes #105501?

Copy link
Member

@compiler-errors compiler-errors left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting solution to this problem. Left a few ideas for ways to harden it, because I'm worried about subtle bugs...

compiler/rustc_hir_typeck/src/generator_interior/mod.rs Outdated Show resolved Hide resolved
compiler/rustc_hir_typeck/src/generator_interior/mod.rs Outdated Show resolved Hide resolved
compiler/rustc_hir_typeck/src/generator_interior/mod.rs Outdated Show resolved Hide resolved
compiler/rustc_hir_typeck/src/generator_interior/mod.rs Outdated Show resolved Hide resolved
// If this is the `&mut Context<'_>` async resume argument, or we are
// just visiting a `_task_context = yield ()` expression from async
// lowering, we do *not* consider this type to be live across yields.
if Some(hir_id) == self.task_context_hid || self.in_await_yield {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to more strongly assert that the thing that we're intentionally not considering live across the yield is a &mut Context<'_>? Maybe assert on the value of ty: Ty<'tcx> above?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do I best assert that though without manually constructing a matching type? I could probably get the expected resume_ty from the generator somehow and use that?

Copy link
Member

@compiler-errors compiler-errors Dec 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't you destructure the type, check if it's a reference to a specific ADT? The Context type is a lang item, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, did that now, though its a bit more complicated than hoped.

Did also rename some things and added more comments.

// If this is the `&mut Context<'_>` async resume argument, or we are
// just visiting a `_task_context = yield ()` expression from async
// lowering, we do *not* consider this type to be live across yields.
if Some(hir_id) == self.task_context_hid || self.in_await_yield {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why we need both of these checks? Shouldn't we always be accessing this context type through the same local variable? Could you drop in_await_yield and maybe match on expr instead?

I'm thinking that it should be a ExprKind::Path or something... you could see that it resolved to the argument we're expecting it to be.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried matching just on the ExprKind::Path of the lhs of the assignment. The problem is also the yield itself, more specifically the () going into it, and the &mut Context<'_> coming out of it.

Fun fact, by skipping the whole expression, it also avoids putting a () into the generator witness in some cases.

@khuey
Copy link
Contributor

khuey commented Dec 14, 2022

This does not fix #105501.

When lowering async constructs to generators, the resume argument is
guaranteed not to be alive across yield points. However the simple
`generator_interior` analysis thinks it is. Because of that, the
resume ty was part of the `GeneratorWitness` and considered to be part
of the generator type, even though it is not really.

This prevented async blocks from being `UnwindSafe`, and possibly `Send`
in some cases.

The code now special cases the fact that the `task_context` of async blocks
is never alive across yield points.
@compiler-errors
Copy link
Member

Given #105915 reverting the PR that regressed the root issue, not sure if we want to land this as-is.

@bors
Copy link
Contributor

bors commented Dec 20, 2022

☔ The latest upstream changes (presumably #105918) made this pull request unmergeable. Please resolve the merge conflicts.

@bjorn3
Copy link
Member

bjorn3 commented Dec 20, 2022

That revert PR probably broke cg_clif again. It is fine for the beta branch, but something like this PR + reverting the revert is necessary for cg_clif.

@Swatinem
Copy link
Contributor Author

I do have an idea for cg_clif. Since it is mostly concerned with the MIR after the generators have been properly transformed, which happens after type checking, it should be possible to:

  • Change the type of _task_context to &mut Context<'_>
  • Replace the call to get_context() with a noop

@bjorn3
Copy link
Member

bjorn3 commented Dec 20, 2022

That would work I think.

@Swatinem
Copy link
Contributor Author

Going to close this for now, I will open a separate PR with just the testcase that is good to have.

@Swatinem Swatinem closed this Dec 20, 2022
@Swatinem Swatinem deleted the async-resume-interior branch February 17, 2024 09:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants