RemainderByZero,
GeneratorResumedAfterReturn,
GeneratorResumedAfterPanic,
+ AsyncResumedAfterReturn,
+ AsyncResumedAfterPanic,
}
/// Type for MIR `Assert` terminator error messages.
"generator resumed after completion",
GeneratorResumedAfterPanic =>
"generator resumed after panicking",
+ AsyncResumedAfterReturn =>
+ "`async fn` resumed after completion",
+ AsyncResumedAfterPanic =>
+ "`async fn` resumed after panic",
Panic { .. } | BoundsCheck { .. } =>
bug!("Unexpected PanicInfo"),
}
use crate::hir::def::{CtorKind, Namespace};
use crate::hir::def_id::DefId;
-use crate::hir;
+use crate::hir::GeneratorKind;
use crate::mir::interpret::{GlobalAlloc, PanicInfo, Scalar};
use crate::mir::visit::MirVisitable;
use crate::ty::adjustment::PointerCast;
/// 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`
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!(
yield_ty: None,
generator_drop: None,
generator_layout: None,
+ generator_kind,
local_decls,
user_type_annotations,
arg_count,
index: index.fold_with(folder),
},
Panic { .. } | Overflow(_) | OverflowNeg | DivisionByZero | RemainderByZero |
- GeneratorResumedAfterReturn | GeneratorResumedAfterPanic =>
+ GeneratorResumedAfterReturn | GeneratorResumedAfterPanic |
+ AsyncResumedAfterReturn | AsyncResumedAfterPanic =>
msg.clone(),
};
Assert { cond: cond.fold_with(folder), expected, msg, target, cleanup }
len.visit_with(visitor) || index.visit_with(visitor),
Panic { .. } | Overflow(_) | OverflowNeg |
DivisionByZero | RemainderByZero |
- GeneratorResumedAfterReturn | GeneratorResumedAfterPanic =>
+ GeneratorResumedAfterReturn | GeneratorResumedAfterPanic |
+ AsyncResumedAfterReturn | AsyncResumedAfterPanic =>
false
}
} else {
self.visit_operand(index, location);
}
Panic { .. } | Overflow(_) | OverflowNeg | DivisionByZero | RemainderByZero |
- GeneratorResumedAfterReturn | GeneratorResumedAfterPanic => {
+ GeneratorResumedAfterReturn | GeneratorResumedAfterPanic |
+ AsyncResumedAfterReturn | AsyncResumedAfterPanic => {
// Nothing to visit
}
}
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;
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.
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,
Safety::Safe,
const_ty,
const_ty_span,
- false,
+ None,
);
let mut block = START_BLOCK;
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()
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 {
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(),
self.var_debug_info,
self.fn_span,
self.hir.control_flow_destroyed(),
+ self.generator_kind
)
}
use rustc_data_structures::fx::FxHashMap;
use std::collections::hash_map::Entry;
use std::mem;
+use rustc::hir::GeneratorKind;
#[derive(Debug)]
struct 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!`?
// 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();
}
unpack!(block = build_scope_drops(
&mut self.cfg,
- self.is_generator,
+ self.generator_kind,
&scope,
block,
unwind_to,
unpack!(block = build_scope_drops(
&mut self.cfg,
- self.is_generator,
+ self.generator_kind,
scope,
block,
unwind_to,
unpack!(block = build_scope_drops(
&mut self.cfg,
- self.is_generator,
+ self.generator_kind,
scope,
block,
unwind_to,
// 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);
}
}
- 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");
}
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
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,
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();
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)
}));
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
// 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)
RemainderByZero => err_panic!(RemainderByZero),
GeneratorResumedAfterReturn => err_panic!(GeneratorResumedAfterReturn),
GeneratorResumedAfterPanic => err_panic!(GeneratorResumedAfterPanic),
+ AsyncResumedAfterReturn => err_panic!(AsyncResumedAfterReturn),
+ AsyncResumedAfterPanic => err_panic!(AsyncResumedAfterPanic),
Panic { .. } => bug!("`Panic` variant cannot occur in MIR"),
}
.into());
vec![],
span,
vec![],
+ None,
);
if let Some(..) = ty {
vec![],
self.span,
vec![],
+ None,
)
}
vec![],
span,
vec![],
+ None,
);
if let Abi::RustCall = sig.abi {
body.spread_arg = Some(Local::new(sig.inputs().len()));
vec![],
span,
vec![],
+ None,
);
crate::util::dump_mir(
Default::default(),
tcx.def_span(source.def_id()),
Default::default(),
+ body.generator_kind,
);
// FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
//! Otherwise it drops all the values in scope at the last suspension point.
use rustc::hir;
-use rustc::hir::def_id::DefId;
+use rustc::hir::{def_id::DefId, GeneratorKind};
use rustc::mir::*;
use rustc::mir::visit::{PlaceContext, Visitor, MutVisitor};
use rustc::ty::{self, TyCtxt, AdtDef, Ty};
use rustc::mir::interpret::PanicInfo::{
GeneratorResumedAfterPanic,
GeneratorResumedAfterReturn,
+ AsyncResumedAfterReturn,
+ AsyncResumedAfterPanic,
};
// Jump to the entry point on the unresumed
cases.insert(0, (UNRESUMED, BasicBlock::new(0)));
- // Panic when resumed on the returned state
- cases.insert(1, (RETURNED, insert_panic_block(tcx, body, GeneratorResumedAfterReturn)));
- // Panic when resumed on the poisoned state
- cases.insert(2, (POISONED, insert_panic_block(tcx, body, GeneratorResumedAfterPanic)));
+
+ // Panic when resumed on the returned or poisoned state
+ match body.generator_kind {
+ Some(GeneratorKind::Async(_)) => {
+ cases.insert(1, (RETURNED, insert_panic_block(tcx, body, AsyncResumedAfterReturn)));
+ cases.insert(2, (POISONED, insert_panic_block(tcx, body, AsyncResumedAfterPanic)));
+ },
+ Some(GeneratorKind::Gen) => {
+ cases.insert(1, (RETURNED, insert_panic_block(tcx, body, GeneratorResumedAfterReturn)));
+ cases.insert(2, (POISONED, insert_panic_block(tcx, body, GeneratorResumedAfterPanic)));
+ },
+ None => {
+ // N/A because we would never create a resume function if there was no generator_kind
+ }
+ };
insert_switch(body, cases, &transform, TerminatorKind::Unreachable);
vec![],
body.span,
vec![],
+ body.generator_kind,
),
tcx,
source: body,
--- /dev/null
+// issue 65419 - Attempting to run an async fn after completion mentions generators when it should
+// be talking about `async fn`s instead.
+
+// run-fail
+// error-pattern: thread 'main' panicked at '`async fn` resumed after completion'
+// compile-flags: --edition 2018
+
+#![feature(generators, generator_trait)]
+
+async fn foo() {
+}
+
+fn main() {
+ let mut future = Box::pin(foo());
+ executor::block_on(future.as_mut());
+ executor::block_on(future.as_mut());
+}
+
+mod executor {
+ use core::{
+ future::Future,
+ pin::Pin,
+ task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
+ };
+
+ pub fn block_on<F: Future>(mut future: F) -> F::Output {
+ let mut future = unsafe { Pin::new_unchecked(&mut future) };
+
+ static VTABLE: RawWakerVTable = RawWakerVTable::new(
+ |_| unimplemented!("clone"),
+ |_| unimplemented!("wake"),
+ |_| unimplemented!("wake_by_ref"),
+ |_| (),
+ );
+ let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+ let mut context = Context::from_waker(&waker);
+
+ loop {
+ if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+ break val;
+ }
+ }
+ }
+}
--- /dev/null
+// issue 65419 - Attempting to run an async fn after completion mentions generators when it should
+// be talking about `async fn`s instead. Should also test what happens when it panics.
+
+// run-fail
+// error-pattern: thread 'main' panicked at '`async fn` resumed after panic'
+// compile-flags: --edition 2018
+
+#![feature(generators, generator_trait)]
+
+use std::panic;
+
+async fn foo() {
+ panic!();
+}
+
+fn main() {
+ let mut future = Box::pin(foo());
+ panic::catch_unwind(panic::AssertUnwindSafe(|| {
+ executor::block_on(future.as_mut());
+ }));
+
+ executor::block_on(future.as_mut());
+}
+
+mod executor {
+ use core::{
+ future::Future,
+ pin::Pin,
+ task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
+ };
+
+ pub fn block_on<F: Future>(mut future: F) -> F::Output {
+ let mut future = unsafe { Pin::new_unchecked(&mut future) };
+
+ static VTABLE: RawWakerVTable = RawWakerVTable::new(
+ |_| unimplemented!("clone"),
+ |_| unimplemented!("wake"),
+ |_| unimplemented!("wake_by_ref"),
+ |_| (),
+ );
+ let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
+ let mut context = Context::from_waker(&waker);
+
+ loop {
+ if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
+ break val;
+ }
+ }
+ }
+}
--- /dev/null
+// issue 65419 - Attempting to run an `async fn` after completion mentions generators when it should
+// be talking about `async fn`s instead. Regression test added to make sure generators still
+// panic when resumed after completion.
+
+// run-fail
+// error-pattern:generator resumed after completion
+
+#![feature(generators, generator_trait)]
+
+use std::{
+ ops::Generator,
+ pin::Pin,
+};
+
+fn main() {
+ let mut g = || {
+ yield;
+ };
+ Pin::new(&mut g).resume(); // Yields once.
+ Pin::new(&mut g).resume(); // Completes here.
+ Pin::new(&mut g).resume(); // Panics here.
+}