Skip to content

Commit

Permalink
Auto merge of #66321 - ninjasource:async-fn-resume-after-completion, …
Browse files Browse the repository at this point in the history
…r=oli-obk

Async fn resume after completion

#65419 -- Attempting to run an async fn after completion mentions generators
Not yet ready for review - work in progress
Just need to run the tests on a proper build server
  • Loading branch information
bors committed Nov 29, 2019
2 parents d99e0c6 + 851492c commit 25d8a94
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 66 deletions.
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

0 comments on commit 25d8a94

Please sign in to comment.