Skip to content

Commit

Permalink
Make async/await lowering use resume arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
jonas-schievink committed Mar 17, 2020
1 parent 18adc45 commit dfcfa17
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 23 deletions.
95 changes: 76 additions & 19 deletions src/librustc_ast_lowering/expr.rs
Expand Up @@ -10,6 +10,7 @@ use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::DUMMY_SP;

impl<'hir> LoweringContext<'_, 'hir> {
fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] {
Expand Down Expand Up @@ -483,14 +484,44 @@ impl<'hir> LoweringContext<'_, 'hir> {
Some(ty) => FnRetTy::Ty(ty),
None => FnRetTy::Default(span),
};
let ast_decl = FnDecl { inputs: vec![], output };

let task_context_id = self.resolver.next_node_id();
let task_context_hid = self.lower_node_id(task_context_id);
let ast_decl = FnDecl {
inputs: vec![Param {
attrs: AttrVec::new(),
ty: AstP(Ty {
id: self.resolver.next_node_id(),
kind: TyKind::Infer,
span: DUMMY_SP,
}),
pat: AstP(Pat {
id: task_context_id,
kind: PatKind::Ident(
BindingMode::ByValue(Mutability::Mut),
Ident::with_dummy_span(sym::_task_context),
None,
),
span: DUMMY_SP,
}),
id: self.resolver.next_node_id(),
span: DUMMY_SP,
is_placeholder: false,
}],
output,
};
let decl = self.lower_fn_decl(&ast_decl, None, /* impl trait allowed */ false, None);
let body_id = self.lower_fn_body(&ast_decl, |this| {
this.generator_kind = Some(hir::GeneratorKind::Async(async_gen_kind));
body(this)

let old_ctx = this.task_context;
this.task_context = Some(task_context_hid);
let res = body(this);
this.task_context = old_ctx;
res
});

// `static || -> <ret_ty> { body }`:
// `static |task_context| -> <ret_ty> { body }`:
let generator_kind = hir::ExprKind::Closure(
capture_clause,
decl,
Expand Down Expand Up @@ -523,9 +554,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// ```rust
/// match <expr> {
/// mut pinned => loop {
/// match ::std::future::poll_with_tls_context(unsafe {
/// <::std::pin::Pin>::new_unchecked(&mut pinned)
/// }) {
/// match unsafe { ::std::future::poll_with_context(
/// <::std::pin::Pin>::new_unchecked(&mut pinned),
/// task_context,
/// ) } {
/// ::std::task::Poll::Ready(result) => break result,
/// ::std::task::Poll::Pending => {}
/// }
Expand Down Expand Up @@ -561,12 +593,23 @@ impl<'hir> LoweringContext<'_, 'hir> {
let (pinned_pat, pinned_pat_hid) =
self.pat_ident_binding_mode(span, pinned_ident, hir::BindingAnnotation::Mutable);

// ::std::future::poll_with_tls_context(unsafe {
// ::std::pin::Pin::new_unchecked(&mut pinned)
// })`
let task_context_ident = Ident::with_dummy_span(sym::_task_context);

// unsafe {
// ::std::future::poll_with_context(
// ::std::pin::Pin::new_unchecked(&mut pinned),
// task_context,
// )
// }
let poll_expr = {
let pinned = self.expr_ident(span, pinned_ident, pinned_pat_hid);
let ref_mut_pinned = self.expr_mut_addr_of(span, pinned);
let task_context = if let Some(task_context_hid) = self.task_context {
self.expr_ident_mut(span, task_context_ident, task_context_hid)
} else {
// Use of `await` outside of an async context, we cannot use `task_context` here.
self.expr_err(span)
};
let pin_ty_id = self.next_id();
let new_unchecked_expr_kind = self.expr_call_std_assoc_fn(
pin_ty_id,
Expand All @@ -575,14 +618,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
"new_unchecked",
arena_vec![self; ref_mut_pinned],
);
let new_unchecked =
self.arena.alloc(self.expr(span, new_unchecked_expr_kind, ThinVec::new()));
let unsafe_expr = self.expr_unsafe(new_unchecked);
self.expr_call_std_path(
let new_unchecked = self.expr(span, new_unchecked_expr_kind, ThinVec::new());
let call = self.expr_call_std_path(
gen_future_span,
&[sym::future, sym::poll_with_tls_context],
arena_vec![self; unsafe_expr],
)
&[sym::future, sym::poll_with_context],
arena_vec![self; new_unchecked, task_context],
);
self.arena.alloc(self.expr_unsafe(call))
};

// `::std::task::Poll::Ready(result) => break result`
Expand Down Expand Up @@ -629,12 +671,27 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Yield(unit, hir::YieldSource::Await),
ThinVec::new(),
);
self.stmt_expr(span, yield_expr)
let yield_expr = self.arena.alloc(yield_expr);

if let Some(task_context_hid) = self.task_context {
let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
let assign = self.expr(
span,
hir::ExprKind::Assign(lhs, yield_expr, span),
AttrVec::new(),
);
self.stmt_expr(span, assign)
} else {
// Use of `await` outside of an async context. Return `yield_expr` so that we can
// proceed with type checking.
self.stmt(span, hir::StmtKind::Semi(yield_expr))
}
};

let loop_block = self.block_all(span, arena_vec![self; inner_match_stmt, yield_stmt], None);
let loop_block =
self.block_all(span, arena_vec![self; inner_match_stmt, yield_stmt], None);

// loop { .. }
// loop { ...; task_context = yield (); }
let loop_expr = self.arena.alloc(hir::Expr {
hir_id: loop_hir_id,
kind: hir::ExprKind::Loop(loop_block, None, hir::LoopSource::Loop),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_ast_lowering/item.rs
Expand Up @@ -809,7 +809,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}

/// Construct `ExprKind::Err` for the given `span`.
fn expr_err(&mut self, span: Span) -> hir::Expr<'hir> {
crate fn expr_err(&mut self, span: Span) -> hir::Expr<'hir> {
self.expr(span, hir::ExprKind::Err, AttrVec::new())
}

Expand Down
5 changes: 5 additions & 0 deletions src/librustc_ast_lowering/lib.rs
Expand Up @@ -117,6 +117,10 @@ struct LoweringContext<'a, 'hir: 'a> {

generator_kind: Option<hir::GeneratorKind>,

/// When inside an `async` context, this is the `HirId` of the
/// `task_context` local bound to the resume argument of the generator.
task_context: Option<hir::HirId>,

/// Used to get the current `fn`'s def span to point to when using `await`
/// outside of an `async fn`.
current_item: Option<Span>,
Expand Down Expand Up @@ -295,6 +299,7 @@ pub fn lower_crate<'a, 'hir>(
item_local_id_counters: Default::default(),
node_id_to_hir_id: IndexVec::new(),
generator_kind: None,
task_context: None,
current_item: None,
lifetimes_to_define: Vec::new(),
is_collecting_in_band_lifetimes: false,
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_span/symbol.rs
Expand Up @@ -544,7 +544,7 @@ symbols! {
plugin_registrar,
plugins,
Poll,
poll_with_tls_context,
poll_with_context,
powerpc_target_feature,
precise_pointer_size_matching,
pref_align_of,
Expand Down Expand Up @@ -720,6 +720,7 @@ symbols! {
target_has_atomic_load_store,
target_thread_local,
task,
_task_context,
tbm_target_feature,
termination_trait,
termination_trait_test,
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/async-await/issues/issue-62009-1.stderr
Expand Up @@ -33,10 +33,10 @@ error[E0277]: the trait bound `[closure@$DIR/issue-62009-1.rs:16:5: 16:15]: std:
LL | (|_| 2333).await;
| ^^^^^^^^^^^^^^^^ the trait `std::future::Future` is not implemented for `[closure@$DIR/issue-62009-1.rs:16:5: 16:15]`
|
::: $SRC_DIR/libstd/future.rs:LL:COL
::: $SRC_DIR/libcore/future/mod.rs:LL:COL
|
LL | F: Future,
| ------ required by this bound in `std::future::poll_with_tls_context`
| ------ required by this bound in `std::future::poll_with_context`

error: aborting due to 4 previous errors

Expand Down

0 comments on commit dfcfa17

Please sign in to comment.