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

Async fn resume after completion #66321

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
Expand Up @@ -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::*;
Expand Down Expand Up @@ -264,8 +264,8 @@ pub enum PanicInfo<O> {
OverflowNeg,
DivisionByZero,
RemainderByZero,
GeneratorResumedAfterReturn,
GeneratorResumedAfterPanic,
ResumedAfterReturn(GeneratorKind),
ResumedAfterPanic(GeneratorKind),
}

/// Type for MIR `Assert` terminator error messages.
Expand Down Expand Up @@ -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"),
}
Expand Down
22 changes: 19 additions & 3 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -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!(
Expand All @@ -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,
Expand Down Expand Up @@ -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 }
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
15 changes: 8 additions & 7 deletions src/librustc_mir/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -647,7 +647,7 @@ fn construct_const<'a, 'tcx>(
Safety::Safe,
const_ty,
const_ty_span,
false,
None,
);

let mut block = START_BLOCK;
Expand Down Expand Up @@ -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()
Expand All @@ -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(),
Expand Down Expand Up @@ -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
)
}

Expand Down
40 changes: 23 additions & 17 deletions src/librustc_mir/build/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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!`?

Expand All @@ -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();
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand All @@ -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)
}));
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions src/librustc_mir/interpret/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Loading