Skip to content

Async fn resume after completion #66321

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

Merged
merged 12 commits into from
Nov 29, 2019
14 changes: 9 additions & 5 deletions src/librustc/mir/interpret/error.rs
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ use rustc_macros::HashStable;
use rustc_target::spec::abi::Abi;
use syntax_pos::{Pos, Span};
use syntax::symbol::Symbol;

use hir::GeneratorKind;
use std::{fmt, env};

use rustc_error_codes::*;
@@ -264,8 +264,8 @@ pub enum PanicInfo<O> {
OverflowNeg,
DivisionByZero,
RemainderByZero,
GeneratorResumedAfterReturn,
GeneratorResumedAfterPanic,
ResumedAfterReturn(GeneratorKind),
ResumedAfterPanic(GeneratorKind),
}

/// Type for MIR `Assert` terminator error messages.
@@ -300,10 +300,14 @@ impl<O> PanicInfo<O> {
"attempt to divide by zero",
RemainderByZero =>
"attempt to calculate the remainder with a divisor of zero",
GeneratorResumedAfterReturn =>
ResumedAfterReturn(GeneratorKind::Gen) =>
"generator resumed after completion",
GeneratorResumedAfterPanic =>
ResumedAfterReturn(GeneratorKind::Async(_)) =>
"`async fn` resumed after completion",
ResumedAfterPanic(GeneratorKind::Gen) =>
"generator resumed after panicking",
ResumedAfterPanic(GeneratorKind::Async(_)) =>
"`async fn` resumed after panicking",
Panic { .. } | BoundsCheck { .. } =>
bug!("Unexpected PanicInfo"),
}
22 changes: 19 additions & 3 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

use crate::hir::def::{CtorKind, Namespace};
use crate::hir::def_id::DefId;
use crate::hir;
use crate::hir::{self, GeneratorKind};
use crate::mir::interpret::{GlobalAlloc, PanicInfo, Scalar};
use crate::mir::visit::MirVisitable;
use crate::ty::adjustment::PointerCast;
@@ -117,6 +117,10 @@ pub struct Body<'tcx> {
/// The layout of a generator. Produced by the state transformation.
pub generator_layout: Option<GeneratorLayout<'tcx>>,

/// If this is a generator then record the type of source expression that caused this generator
/// to be created.
pub generator_kind: Option<GeneratorKind>,

/// Declarations of locals.
///
/// The first local is the return value pointer, followed by `arg_count`
@@ -170,6 +174,7 @@ impl<'tcx> Body<'tcx> {
var_debug_info: Vec<VarDebugInfo<'tcx>>,
span: Span,
control_flow_destroyed: Vec<(Span, String)>,
generator_kind : Option<GeneratorKind>,
) -> Self {
// We need `arg_count` locals, and one for the return place.
assert!(
@@ -187,6 +192,7 @@ impl<'tcx> Body<'tcx> {
yield_ty: None,
generator_drop: None,
generator_layout: None,
generator_kind,
local_decls,
user_type_annotations,
arg_count,
@@ -2975,7 +2981,7 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
index: index.fold_with(folder),
},
Panic { .. } | Overflow(_) | OverflowNeg | DivisionByZero | RemainderByZero |
GeneratorResumedAfterReturn | GeneratorResumedAfterPanic =>
ResumedAfterReturn(_) | ResumedAfterPanic(_) =>
msg.clone(),
};
Assert { cond: cond.fold_with(folder), expected, msg, target, cleanup }
@@ -3021,7 +3027,7 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
len.visit_with(visitor) || index.visit_with(visitor),
Panic { .. } | Overflow(_) | OverflowNeg |
DivisionByZero | RemainderByZero |
GeneratorResumedAfterReturn | GeneratorResumedAfterPanic =>
ResumedAfterReturn(_) | ResumedAfterPanic(_) =>
false
}
} else {
@@ -3040,6 +3046,16 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
}
}

impl<'tcx> TypeFoldable<'tcx> for GeneratorKind {
fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
*self
}

fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> bool {
false
}
}

impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> {
fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
Place {
2 changes: 1 addition & 1 deletion src/librustc/mir/visit.rs
Original file line number Diff line number Diff line change
@@ -517,7 +517,7 @@ macro_rules! make_mir_visitor {
self.visit_operand(index, location);
}
Panic { .. } | Overflow(_) | OverflowNeg | DivisionByZero | RemainderByZero |
GeneratorResumedAfterReturn | GeneratorResumedAfterPanic => {
ResumedAfterReturn(_) | ResumedAfterPanic(_) => {
// Nothing to visit
}
}
15 changes: 8 additions & 7 deletions src/librustc_mir/build/mod.rs
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ use crate::hair::{LintLevel, BindingMode, PatKind};
use crate::transform::MirSource;
use crate::util as mir_util;
use rustc::hir;
use rustc::hir::Node;
use rustc::hir::{Node, GeneratorKind};
use rustc::hir::def_id::DefId;
use rustc::middle::lang_items;
use rustc::middle::region;
@@ -279,7 +279,7 @@ struct Builder<'a, 'tcx> {

fn_span: Span,
arg_count: usize,
is_generator: bool,
generator_kind: Option<GeneratorKind>,

/// The current set of scopes, updated as we traverse;
/// see the `scope` module for more details.
@@ -570,7 +570,7 @@ where
safety,
return_ty,
return_ty_span,
body.generator_kind.is_some());
body.generator_kind);

let call_site_scope = region::Scope {
id: body.value.hir_id.local_id,
@@ -647,7 +647,7 @@ fn construct_const<'a, 'tcx>(
Safety::Safe,
const_ty,
const_ty_span,
false,
None,
);

let mut block = START_BLOCK;
@@ -678,7 +678,7 @@ fn construct_error<'a, 'tcx>(
let owner_id = hir.tcx().hir().body_owner(body_id);
let span = hir.tcx().hir().span(owner_id);
let ty = hir.tcx().types.err;
let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, span, false);
let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, span, None);
let source_info = builder.source_info(span);
builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
builder.finish()
@@ -691,15 +691,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
safety: Safety,
return_ty: Ty<'tcx>,
return_span: Span,
is_generator: bool)
generator_kind: Option<GeneratorKind>)
-> Builder<'a, 'tcx> {
let lint_level = LintLevel::Explicit(hir.root_lint_level);
let mut builder = Builder {
hir,
cfg: CFG { basic_blocks: IndexVec::new() },
fn_span: span,
arg_count,
is_generator,
generator_kind,
scopes: Default::default(),
block_context: BlockContext::new(),
source_scopes: IndexVec::new(),
@@ -748,6 +748,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.var_debug_info,
self.fn_span,
self.hir.control_flow_destroyed(),
self.generator_kind
)
}

40 changes: 23 additions & 17 deletions src/librustc_mir/build/scope.rs
Original file line number Diff line number Diff line change
@@ -91,6 +91,7 @@ use syntax_pos::{DUMMY_SP, Span};
use rustc_data_structures::fx::FxHashMap;
use std::collections::hash_map::Entry;
use std::mem;
use rustc::hir::GeneratorKind;

#[derive(Debug)]
struct Scope {
@@ -219,7 +220,12 @@ impl Scope {
/// `storage_only` controls whether to invalidate only drop paths that run `StorageDead`.
/// `this_scope_only` controls whether to invalidate only drop paths that refer to the current
/// top-of-scope (as opposed to dependent scopes).
fn invalidate_cache(&mut self, storage_only: bool, is_generator: bool, this_scope_only: bool) {
fn invalidate_cache(
&mut self,
storage_only: bool,
generator_kind: Option<GeneratorKind>,
this_scope_only: bool
) {
// FIXME: maybe do shared caching of `cached_exits` etc. to handle functions
// with lots of `try!`?

@@ -229,7 +235,7 @@ impl Scope {
// the current generator drop and unwind refer to top-of-scope
self.cached_generator_drop = None;

let ignore_unwinds = storage_only && !is_generator;
let ignore_unwinds = storage_only && generator_kind.is_none();
if !ignore_unwinds {
self.cached_unwind.invalidate();
}
@@ -481,7 +487,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {

unpack!(block = build_scope_drops(
&mut self.cfg,
self.is_generator,
self.generator_kind,
&scope,
block,
unwind_to,
@@ -574,7 +580,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {

unpack!(block = build_scope_drops(
&mut self.cfg,
self.is_generator,
self.generator_kind,
scope,
block,
unwind_to,
@@ -625,7 +631,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {

unpack!(block = build_scope_drops(
&mut self.cfg,
self.is_generator,
self.generator_kind,
scope,
block,
unwind_to,
@@ -809,7 +815,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// invalidating caches of each scope visited. This way bare minimum of the
// caches gets invalidated. i.e., if a new drop is added into the middle scope, the
// cache of outer scope stays intact.
scope.invalidate_cache(!needs_drop, self.is_generator, this_scope);
scope.invalidate_cache(!needs_drop, self.generator_kind, this_scope);
if this_scope {
let region_scope_span = region_scope.span(self.hir.tcx(),
&self.hir.region_scope_tree);
@@ -958,7 +964,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}

top_scope.invalidate_cache(true, self.is_generator, true);
top_scope.invalidate_cache(true, self.generator_kind, true);
} else {
bug!("Expected as_local_operand to produce a temporary");
}
@@ -1016,7 +1022,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {

for scope in self.scopes.top_scopes(first_uncached) {
target = build_diverge_scope(&mut self.cfg, scope.region_scope_span,
scope, target, generator_drop, self.is_generator);
scope, target, generator_drop, self.generator_kind);
}

target
@@ -1079,14 +1085,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
assert_eq!(top_scope.region_scope, region_scope);

top_scope.drops.clear();
top_scope.invalidate_cache(false, self.is_generator, true);
top_scope.invalidate_cache(false, self.generator_kind, true);
}
}

/// Builds drops for pop_scope and exit_scope.
fn build_scope_drops<'tcx>(
cfg: &mut CFG<'tcx>,
is_generator: bool,
generator_kind: Option<GeneratorKind>,
scope: &Scope,
mut block: BasicBlock,
last_unwind_to: BasicBlock,
@@ -1130,7 +1136,7 @@ fn build_scope_drops<'tcx>(
continue;
}

let unwind_to = get_unwind_to(scope, is_generator, drop_idx, generator_drop)
let unwind_to = get_unwind_to(scope, generator_kind, drop_idx, generator_drop)
.unwrap_or(last_unwind_to);

let next = cfg.start_new_block();
@@ -1156,19 +1162,19 @@ fn build_scope_drops<'tcx>(

fn get_unwind_to(
scope: &Scope,
is_generator: bool,
generator_kind: Option<GeneratorKind>,
unwind_from: usize,
generator_drop: bool,
) -> Option<BasicBlock> {
for drop_idx in (0..unwind_from).rev() {
let drop_data = &scope.drops[drop_idx];
match (is_generator, &drop_data.kind) {
(true, DropKind::Storage) => {
match (generator_kind, &drop_data.kind) {
(Some(_), DropKind::Storage) => {
return Some(drop_data.cached_block.get(generator_drop).unwrap_or_else(|| {
span_bug!(drop_data.span, "cached block not present for {:?}", drop_data)
}));
}
(false, DropKind::Value) => {
(None, DropKind::Value) => {
return Some(drop_data.cached_block.get(generator_drop).unwrap_or_else(|| {
span_bug!(drop_data.span, "cached block not present for {:?}", drop_data)
}));
@@ -1184,7 +1190,7 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
scope: &mut Scope,
mut target: BasicBlock,
generator_drop: bool,
is_generator: bool)
generator_kind: Option<GeneratorKind>)
-> BasicBlock
{
// Build up the drops in **reverse** order. The end result will
@@ -1224,7 +1230,7 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
// match the behavior of clang, but on inspection eddyb says
// this is not what clang does.
match drop_data.kind {
DropKind::Storage if is_generator => {
DropKind::Storage if generator_kind.is_some() => {
storage_deads.push(Statement {
source_info: source_info(drop_data.span),
kind: StatementKind::StorageDead(drop_data.local)
6 changes: 4 additions & 2 deletions src/librustc_mir/interpret/terminator.rs
Original file line number Diff line number Diff line change
@@ -142,8 +142,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
OverflowNeg => err_panic!(OverflowNeg),
DivisionByZero => err_panic!(DivisionByZero),
RemainderByZero => err_panic!(RemainderByZero),
GeneratorResumedAfterReturn => err_panic!(GeneratorResumedAfterReturn),
GeneratorResumedAfterPanic => err_panic!(GeneratorResumedAfterPanic),
ResumedAfterReturn(generator_kind)
=> err_panic!(ResumedAfterReturn(*generator_kind)),
ResumedAfterPanic(generator_kind)
=> err_panic!(ResumedAfterPanic(*generator_kind)),
Panic { .. } => bug!("`Panic` variant cannot occur in MIR"),
}
.into());
Loading