Auto merge of #104321 - Swatinem:async-gen, r=oli-obk

Avoid `GenFuture` shim when compiling async constructs

Previously, async constructs would be lowered to "normal" generators, with an additional `from_generator` / `GenFuture` shim in between to convert from `Generator` to `Future`.

The compiler will now special-case these generators internally so that async constructs will *directly* implement `Future` without the need to go through the `from_generator` / `GenFuture` shim.

The primary motivation for this change was hiding this implementation detail in stack traces and debuginfo, but it can in theory also help the optimizer as there is less abstractions to see through.

---

Given this demo code:

```rust
pub async fn a(arg: u32) -> Backtrace {
    let bt = b().await;
    let _arg = arg;
    bt
}

pub async fn b() -> Backtrace {
    Backtrace::force_capture()
}
```

I would get the following with the latest stable compiler (on Windows):

```
   4: async_codegen:🅱️:async_fn$0
             at .\src\lib.rs:10
   5: core::future::from_generator::impl$1::poll<enum2$<async_codegen:🅱️:async_fn_env$0> >
             at /rustc/897e37553bba8b42751c67658967889d11ecd120\library\core\src\future\mod.rs:91
   6: async_codegen:🅰️:async_fn$0
             at .\src\lib.rs:4
   7: core::future::from_generator::impl$1::poll<enum2$<async_codegen:🅰️:async_fn_env$0> >
             at /rustc/897e37553bba8b42751c67658967889d11ecd120\library\core\src\future\mod.rs:91
```

whereas now I get a much cleaner stack trace:

```
   3: async_codegen:🅱️:async_fn$0
             at .\src\lib.rs:10
   4: async_codegen:🅰️:async_fn$0
             at .\src\lib.rs:4
```
This commit is contained in:
bors 2022-11-24 17:14:42 +00:00
commit 5dfb4b0afa
41 changed files with 459 additions and 211 deletions

View file

@ -149,7 +149,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
*capture_clause,
*closure_node_id,
None,
block.span,
e.span,
hir::AsyncGeneratorKind::Block,
|this| this.with_new_scopes(|this| this.lower_block_expr(block)),
),
@ -569,12 +569,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
/// Lower an `async` construct to a generator that is then wrapped so it implements `Future`.
/// Lower an `async` construct to a generator that implements `Future`.
///
/// This results in:
///
/// ```text
/// std::future::from_generator(static move? |_task_context| -> <ret_ty> {
/// std::future::identity_future(static move? |_task_context| -> <ret_ty> {
/// <body>
/// })
/// ```
@ -589,12 +589,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
) -> hir::ExprKind<'hir> {
let output = ret_ty.unwrap_or_else(|| hir::FnRetTy::DefaultReturn(self.lower_span(span)));
// Resume argument type. We let the compiler infer this to simplify the lowering. It is
// fully constrained by `future::from_generator`.
// Resume argument type: `ResumeTy`
let unstable_span =
self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
let resume_ty = hir::QPath::LangItem(hir::LangItem::ResumeTy, unstable_span, None);
let input_ty = hir::Ty {
hir_id: self.next_id(),
kind: hir::TyKind::Infer,
span: self.lower_span(span),
kind: hir::TyKind::Path(resume_ty),
span: unstable_span,
};
// The closure/generator `FnDecl` takes a single (resume) argument of type `input_ty`.
@ -677,16 +679,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
let generator = hir::Expr { hir_id, kind: generator_kind, span: self.lower_span(span) };
// `future::from_generator`:
let gen_future = self.expr_lang_item_path(
// FIXME(swatinem):
// For some reason, the async block needs to flow through *any*
// call (like the identity function), as otherwise type and lifetime
// inference have a hard time figuring things out.
// Without this, we would get:
// E0720 in src/test/ui/impl-trait/in-trait/default-body-with-rpit.rs
// E0700 in src/test/ui/self/self_lifetime-async.rs
// `future::identity_future`:
let identity_future = self.expr_lang_item_path(
unstable_span,
hir::LangItem::FromGenerator,
hir::LangItem::IdentityFuture,
AttrVec::new(),
None,
);
// `future::from_generator(generator)`:
hir::ExprKind::Call(self.arena.alloc(gen_future), arena_vec![self; generator])
// `future::identity_future(generator)`:
hir::ExprKind::Call(self.arena.alloc(identity_future), arena_vec![self; generator])
}
/// Desugar `<expr>.await` into:
@ -990,7 +1000,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
// Transform `async |x: u8| -> X { ... }` into
// `|x: u8| future_from_generator(|| -> X { ... })`.
// `|x: u8| identity_future(|| -> X { ... })`.
let body_id = this.lower_fn_body(&outer_decl, |this| {
let async_ret_ty = if let FnRetTy::Ty(ty) = &decl.output {
let itctx = ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock);

View file

@ -21,7 +21,7 @@ use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::Region;
use rustc_middle::ty::TypeVisitor;
use rustc_middle::ty::{self, RegionVid, Ty};
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::symbol::{kw, Ident};
use rustc_span::Span;
use crate::borrowck_errors;
@ -514,8 +514,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
span: *span,
ty_err: match output_ty.kind() {
ty::Closure(_, _) => FnMutReturnTypeErr::ReturnClosure { span: *span },
ty::Adt(def, _)
if self.infcx.tcx.is_diagnostic_item(sym::gen_future, def.did()) =>
ty::Generator(def, ..)
if matches!(
self.infcx.tcx.generator_kind(def),
Some(hir::GeneratorKind::Async(_))
) =>
{
FnMutReturnTypeErr::ReturnAsyncBlock { span: *span }
}
@ -927,10 +930,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
// only when the block is a closure
if let hir::ExprKind::Closure(hir::Closure {
capture_clause: hir::CaptureBy::Ref,
body,
..
}) = expr.kind
{
closure_span = Some(expr.span.shrink_to_lo());
let body = map.body(*body);
if !matches!(body.generator_kind, Some(hir::GeneratorKind::Async(..))) {
closure_span = Some(expr.span.shrink_to_lo());
}
}
}
}

View file

@ -2588,7 +2588,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
// For closures, we have some **extra requirements** we
//
// have to check. In particular, in their upvars and
// signatures, closures often reference various regions
// from the surrounding function -- we call those the

View file

@ -449,8 +449,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
| Rvalue::CopyForDeref(..)
| Rvalue::Repeat(..)
| Rvalue::Discriminant(..)
| Rvalue::Len(_)
| Rvalue::Aggregate(..) => {}
| Rvalue::Len(_) => {}
Rvalue::Aggregate(ref kind, ..) => {
if let AggregateKind::Generator(def_id, ..) = kind.as_ref() {
if let Some(generator_kind) = self.tcx.generator_kind(def_id.to_def_id()) {
if matches!(generator_kind, hir::GeneratorKind::Async(..)) {
self.check_op(ops::Generator(generator_kind));
}
}
}
}
Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place)
| Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place) => {
@ -889,14 +898,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
return;
}
// `async` blocks get lowered to `std::future::from_generator(/* a closure */)`.
let is_async_block = Some(callee) == tcx.lang_items().from_generator_fn();
if is_async_block {
let kind = hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block);
self.check_op(ops::Generator(kind));
return;
}
if !tcx.is_const_fn_raw(callee) {
if !tcx.is_const_default_method(callee) {
// To get to here we must have already found a const impl for the

View file

@ -280,10 +280,14 @@ language_item_table! {
PointerSized, sym::pointer_sized, pointer_sized, Target::Trait, GenericRequirement::Exact(0);
Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None;
PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
FromGenerator, sym::from_generator, from_generator_fn, Target::Fn, GenericRequirement::None;
// FIXME(swatinem): the following lang items are used for async lowering and
// should become obsolete eventually.
ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
IdentityFuture, sym::identity_future, identity_future_fn, Target::Fn, GenericRequirement::None;
GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None;
FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;

View file

@ -56,10 +56,15 @@ pub(super) fn check_fn<'a, 'tcx>(
fn_maybe_err(tcx, span, fn_sig.abi);
if body.generator_kind.is_some() && can_be_generator.is_some() {
let yield_ty = fcx
.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span });
fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
if let Some(kind) = body.generator_kind && can_be_generator.is_some() {
let yield_ty = if kind == hir::GeneratorKind::Gen {
let yield_ty = fcx
.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span });
fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
yield_ty
} else {
tcx.mk_unit()
};
// Resume type defaults to `()` if the generator has no argument.
let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit());

View file

@ -1734,14 +1734,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let hir = self.tcx.hir();
let hir::Node::Expr(expr) = hir.get(hir_id) else { return false; };
// Skip over mentioning async lang item
if Some(def_id) == self.tcx.lang_items().from_generator_fn()
&& error.obligation.cause.span.desugaring_kind()
== Some(rustc_span::DesugaringKind::Async)
{
return false;
}
let Some(unsubstituted_pred) =
self.tcx.predicates_of(def_id).instantiate_identity(self.tcx).predicates.into_iter().nth(idx)
else { return false; };

View file

@ -319,7 +319,20 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
.map(|inner| MustUsePath::Array(Box::new(inner), len)),
},
ty::Closure(..) => Some(MustUsePath::Closure(span)),
ty::Generator(..) => Some(MustUsePath::Generator(span)),
ty::Generator(def_id, ..) => {
// async fn should be treated as "implementor of `Future`"
let must_use = if matches!(
cx.tcx.generator_kind(def_id),
Some(hir::GeneratorKind::Async(..))
) {
let def_id = cx.tcx.lang_items().future_trait().unwrap();
is_def_must_use(cx, def_id, span)
.map(|inner| MustUsePath::Opaque(Box::new(inner)))
} else {
None
};
must_use.or(Some(MustUsePath::Generator(span)))
}
_ => None,
}
}

View file

@ -660,6 +660,9 @@ pub enum ImplSource<'tcx, N> {
/// ImplSource automatically generated for a generator.
Generator(ImplSourceGeneratorData<'tcx, N>),
/// ImplSource automatically generated for a generator backing an async future.
Future(ImplSourceFutureData<'tcx, N>),
/// ImplSource for a trait alias.
TraitAlias(ImplSourceTraitAliasData<'tcx, N>),
@ -676,6 +679,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
ImplSource::AutoImpl(d) => d.nested,
ImplSource::Closure(c) => c.nested,
ImplSource::Generator(c) => c.nested,
ImplSource::Future(c) => c.nested,
ImplSource::Object(d) => d.nested,
ImplSource::FnPointer(d) => d.nested,
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
@ -694,6 +698,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
ImplSource::AutoImpl(d) => &d.nested,
ImplSource::Closure(c) => &c.nested,
ImplSource::Generator(c) => &c.nested,
ImplSource::Future(c) => &c.nested,
ImplSource::Object(d) => &d.nested,
ImplSource::FnPointer(d) => &d.nested,
ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
@ -737,6 +742,11 @@ impl<'tcx, N> ImplSource<'tcx, N> {
substs: c.substs,
nested: c.nested.into_iter().map(f).collect(),
}),
ImplSource::Future(c) => ImplSource::Future(ImplSourceFutureData {
generator_def_id: c.generator_def_id,
substs: c.substs,
nested: c.nested.into_iter().map(f).collect(),
}),
ImplSource::FnPointer(p) => ImplSource::FnPointer(ImplSourceFnPointerData {
fn_ty: p.fn_ty,
nested: p.nested.into_iter().map(f).collect(),
@ -796,6 +806,16 @@ pub struct ImplSourceGeneratorData<'tcx, N> {
pub nested: Vec<N>,
}
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
#[derive(TypeFoldable, TypeVisitable)]
pub struct ImplSourceFutureData<'tcx, N> {
pub generator_def_id: DefId,
pub substs: SubstsRef<'tcx>,
/// Nested obligations. This can be non-empty if the generator
/// signature contains associated types.
pub nested: Vec<N>,
}
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
#[derive(TypeFoldable, TypeVisitable)]
pub struct ImplSourceClosureData<'tcx, N> {

View file

@ -131,6 +131,10 @@ pub enum SelectionCandidate<'tcx> {
/// generated for a generator.
GeneratorCandidate,
/// Implementation of a `Future` trait by one of the generator types
/// generated for an async construct.
FutureCandidate,
/// Implementation of a `Fn`-family trait by one of the anonymous
/// types generated for a fn pointer type (e.g., `fn(int) -> int`)
FnPointerCandidate {

View file

@ -15,6 +15,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {
super::ImplSource::Generator(ref d) => write!(f, "{:?}", d),
super::ImplSource::Future(ref d) => write!(f, "{:?}", d),
super::ImplSource::FnPointer(ref d) => write!(f, "({:?})", d),
super::ImplSource::DiscriminantKind(ref d) => write!(f, "{:?}", d),
@ -58,6 +60,16 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceGeneratorData<'tcx, N
}
}
impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceFutureData<'tcx, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ImplSourceFutureData(generator_def_id={:?}, substs={:?}, nested={:?})",
self.generator_def_id, self.substs, self.nested
)
}
}
impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceClosureData<'tcx, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(

View file

@ -681,6 +681,17 @@ pub trait PrettyPrinter<'tcx>:
}
ty::Str => p!("str"),
ty::Generator(did, substs, movability) => {
// FIXME(swatinem): async constructs used to be pretty printed
// as `impl Future` previously due to the `from_generator` wrapping.
// lets special case this here for now to avoid churn in diagnostics.
let generator_kind = self.tcx().generator_kind(did);
if matches!(generator_kind, Some(hir::GeneratorKind::Async(..))) {
let return_ty = substs.as_generator().return_ty();
p!(write("impl Future<Output = {}>", return_ty));
return Ok(self);
}
p!(write("["));
match movability {
hir::Movability::Movable => {}

View file

@ -11,10 +11,10 @@
//! generator in the MIR, since it is used to create the drop glue for the generator. We'd get
//! infinite recursion otherwise.
//!
//! This pass creates the implementation for the Generator::resume function and the drop shim
//! for the generator based on the MIR input. It converts the generator argument from Self to
//! &mut Self adding derefs in the MIR as needed. It computes the final layout of the generator
//! struct which looks like this:
//! This pass creates the implementation for either the `Generator::resume` or `Future::poll`
//! function and the drop shim for the generator based on the MIR input.
//! It converts the generator argument from Self to &mut Self adding derefs in the MIR as needed.
//! It computes the final layout of the generator struct which looks like this:
//! First upvars are stored
//! It is followed by the generator state field.
//! Then finally the MIR locals which are live across a suspension point are stored.
@ -32,14 +32,15 @@
//! 2 - Generator has been poisoned
//!
//! It also rewrites `return x` and `yield y` as setting a new generator state and returning
//! GeneratorState::Complete(x) and GeneratorState::Yielded(y) respectively.
//! `GeneratorState::Complete(x)` and `GeneratorState::Yielded(y)`,
//! or `Poll::Ready(x)` and `Poll::Pending` respectively.
//! MIR locals which are live across a suspension point are moved to the generator struct
//! with references to them being updated with references to the generator struct.
//!
//! The pass creates two functions which have a switch on the generator state giving
//! the action to take.
//!
//! One of them is the implementation of Generator::resume.
//! One of them is the implementation of `Generator::resume` / `Future::poll`.
//! For generators with state 0 (unresumed) it starts the execution of the generator.
//! For generators with state 1 (returned) and state 2 (poisoned) it panics.
//! Otherwise it continues the execution from the last suspension point.
@ -56,6 +57,7 @@ use crate::MirPass;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
use rustc_hir::GeneratorKind;
use rustc_index::bit_set::{BitMatrix, BitSet, GrowableBitSet};
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::dump_mir;
@ -215,6 +217,7 @@ struct SuspensionPoint<'tcx> {
struct TransformVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
is_async_kind: bool,
state_adt_ref: AdtDef<'tcx>,
state_substs: SubstsRef<'tcx>,
@ -239,28 +242,57 @@ struct TransformVisitor<'tcx> {
}
impl<'tcx> TransformVisitor<'tcx> {
// Make a GeneratorState variant assignment. `core::ops::GeneratorState` only has single
// element tuple variants, so we can just write to the downcasted first field and then set the
// Make a `GeneratorState` or `Poll` variant assignment.
//
// `core::ops::GeneratorState` only has single element tuple variants,
// so we can just write to the downcasted first field and then set the
// discriminant to the appropriate variant.
fn make_state(
&self,
idx: VariantIdx,
val: Operand<'tcx>,
source_info: SourceInfo,
) -> impl Iterator<Item = Statement<'tcx>> {
is_return: bool,
statements: &mut Vec<Statement<'tcx>>,
) {
let idx = VariantIdx::new(match (is_return, self.is_async_kind) {
(true, false) => 1, // GeneratorState::Complete
(false, false) => 0, // GeneratorState::Yielded
(true, true) => 0, // Poll::Ready
(false, true) => 1, // Poll::Pending
});
let kind = AggregateKind::Adt(self.state_adt_ref.did(), idx, self.state_substs, None, None);
// `Poll::Pending`
if self.is_async_kind && idx == VariantIdx::new(1) {
assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 0);
// FIXME(swatinem): assert that `val` is indeed unit?
statements.extend(expand_aggregate(
Place::return_place(),
std::iter::empty(),
kind,
source_info,
self.tcx,
));
return;
}
// else: `Poll::Ready(x)`, `GeneratorState::Yielded(x)` or `GeneratorState::Complete(x)`
assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 1);
let ty = self
.tcx
.bound_type_of(self.state_adt_ref.variant(idx).fields[0].did)
.subst(self.tcx, self.state_substs);
expand_aggregate(
statements.extend(expand_aggregate(
Place::return_place(),
std::iter::once((val, ty)),
kind,
source_info,
self.tcx,
)
));
}
// Create a Place referencing a generator struct field
@ -331,22 +363,19 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
});
let ret_val = match data.terminator().kind {
TerminatorKind::Return => Some((
VariantIdx::new(1),
None,
Operand::Move(Place::from(self.new_ret_local)),
None,
)),
TerminatorKind::Return => {
Some((true, None, Operand::Move(Place::from(self.new_ret_local)), None))
}
TerminatorKind::Yield { ref value, resume, resume_arg, drop } => {
Some((VariantIdx::new(0), Some((resume, resume_arg)), value.clone(), drop))
Some((false, Some((resume, resume_arg)), value.clone(), drop))
}
_ => None,
};
if let Some((state_idx, resume, v, drop)) = ret_val {
if let Some((is_return, resume, v, drop)) = ret_val {
let source_info = data.terminator().source_info;
// We must assign the value first in case it gets declared dead below
data.statements.extend(self.make_state(state_idx, v, source_info));
self.make_state(v, source_info, is_return, &mut data.statements);
let state = if let Some((resume, mut resume_arg)) = resume {
// Yield
let state = RESERVED_VARIANTS + self.suspension_points.len();
@ -1268,10 +1297,20 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
}
};
// Compute GeneratorState<yield_ty, return_ty>
let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
let state_adt_ref = tcx.adt_def(state_did);
let state_substs = tcx.intern_substs(&[yield_ty.into(), body.return_ty().into()]);
let is_async_kind = body.generator_kind().unwrap() != GeneratorKind::Gen;
let (state_adt_ref, state_substs) = if is_async_kind {
// Compute Poll<return_ty>
let state_did = tcx.require_lang_item(LangItem::Poll, None);
let state_adt_ref = tcx.adt_def(state_did);
let state_substs = tcx.intern_substs(&[body.return_ty().into()]);
(state_adt_ref, state_substs)
} else {
// Compute GeneratorState<yield_ty, return_ty>
let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
let state_adt_ref = tcx.adt_def(state_did);
let state_substs = tcx.intern_substs(&[yield_ty.into(), body.return_ty().into()]);
(state_adt_ref, state_substs)
};
let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
// We rename RETURN_PLACE which has type mir.return_ty to new_ret_local
@ -1327,9 +1366,11 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
// Run the transformation which converts Places from Local to generator struct
// accesses for locals in `remap`.
// It also rewrites `return x` and `yield y` as writing a new generator state and returning
// GeneratorState::Complete(x) and GeneratorState::Yielded(y) respectively.
// either GeneratorState::Complete(x) and GeneratorState::Yielded(y),
// or Poll::Ready(x) and Poll::Pending respectively depending on `is_async_kind`.
let mut transform = TransformVisitor {
tcx,
is_async_kind,
state_adt_ref,
state_substs,
remap,
@ -1367,7 +1408,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
body.generator.as_mut().unwrap().generator_drop = Some(drop_shim);
// Create the Generator::resume function
// Create the Generator::resume / Future::poll function
create_generator_resume_function(tcx, transform, body, can_return);
// Run derefer to fix Derefs that are not in the first place

View file

@ -3914,7 +3914,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
visit::walk_expr(self, expr);
self.diagnostic_metadata.current_type_ascription.pop();
}
// `async |x| ...` gets desugared to `|x| future_from_generator(|| ...)`, so we need to
// `async |x| ...` gets desugared to `|x| async {...}`, so we need to
// resolve the arguments within the proper scopes so that usages of them inside the
// closure are detected as upvars rather than normal closure arg usages.
ExprKind::Closure(box ast::Closure {

View file

@ -264,6 +264,7 @@ symbols! {
Relaxed,
Release,
Result,
ResumeTy,
Return,
Right,
Rust,
@ -728,7 +729,6 @@ symbols! {
frem_fast,
from,
from_desugaring,
from_generator,
from_iter,
from_method,
from_output,
@ -779,6 +779,7 @@ symbols! {
i64,
i8,
ident,
identity_future,
if_let,
if_let_guard,
if_while_or_patterns,

View file

@ -1885,13 +1885,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
//
// - `BuiltinDerivedObligation` with a generator witness (B)
// - `BuiltinDerivedObligation` with a generator (B)
// - `BuiltinDerivedObligation` with `std::future::GenFuture` (B)
// - `BuiltinDerivedObligation` with `impl std::future::Future` (B)
// - `BuiltinDerivedObligation` with `impl std::future::Future` (B)
// - `BuiltinDerivedObligation` with a generator witness (A)
// - `BuiltinDerivedObligation` with a generator (A)
// - `BuiltinDerivedObligation` with `std::future::GenFuture` (A)
// - `BuiltinDerivedObligation` with `impl std::future::Future` (A)
// - `BuiltinDerivedObligation` with `impl std::future::Future` (A)
// - `BindingObligation` with `impl_send (Send requirement)
//
@ -2624,30 +2620,24 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
};
let from_generator = tcx.require_lang_item(LangItem::FromGenerator, None);
let identity_future = tcx.require_lang_item(LangItem::IdentityFuture, None);
// Don't print the tuple of capture types
'print: {
if !is_upvar_tys_infer_tuple {
let msg = format!("required because it appears within the type `{}`", ty);
match ty.kind() {
ty::Adt(def, _) => {
// `gen_future` is used in all async functions; it doesn't add any additional info.
if self.tcx.is_diagnostic_item(sym::gen_future, def.did()) {
break 'print;
}
match self.tcx.opt_item_ident(def.did()) {
Some(ident) => err.span_note(ident.span, &msg),
None => err.note(&msg),
}
}
ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) {
Some(ident) => err.span_note(ident.span, &msg),
None => err.note(&msg),
},
ty::Opaque(def_id, _) => {
// Avoid printing the future from `core::future::from_generator`, it's not helpful
if tcx.parent(*def_id) == from_generator {
// Avoid printing the future from `core::future::identity_future`, it's not helpful
if tcx.parent(*def_id) == identity_future {
break 'print;
}
// If the previous type is `from_generator`, this is the future generated by the body of an async function.
// If the previous type is `identity_future`, this is the future generated by the body of an async function.
// Avoid printing it twice (it was already printed in the `ty::Generator` arm below).
let is_future = tcx.ty_is_opaque_future(ty);
debug!(
@ -2657,8 +2647,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
);
if is_future
&& obligated_types.last().map_or(false, |ty| match ty.kind() {
ty::Opaque(last_def_id, _) => {
tcx.parent(*last_def_id) == from_generator
ty::Generator(last_def_id, ..) => {
matches!(
tcx.generator_kind(last_def_id),
Some(GeneratorKind::Async(..))
)
}
_ => false,
})

View file

@ -12,7 +12,8 @@ use super::SelectionContext;
use super::SelectionError;
use super::{
ImplSourceClosureData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData,
ImplSourceGeneratorData, ImplSourcePointeeData, ImplSourceUserDefinedData,
ImplSourceFutureData, ImplSourceGeneratorData, ImplSourcePointeeData,
ImplSourceUserDefinedData,
};
use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
@ -1544,6 +1545,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
let eligible = match &impl_source {
super::ImplSource::Closure(_)
| super::ImplSource::Generator(_)
| super::ImplSource::Future(_)
| super::ImplSource::FnPointer(_)
| super::ImplSource::TraitAlias(_) => true,
super::ImplSource::UserDefined(impl_data) => {
@ -1832,6 +1834,7 @@ fn confirm_select_candidate<'cx, 'tcx>(
match impl_source {
super::ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
super::ImplSource::Generator(data) => confirm_generator_candidate(selcx, obligation, data),
super::ImplSource::Future(data) => confirm_future_candidate(selcx, obligation, data),
super::ImplSource::Closure(data) => confirm_closure_candidate(selcx, obligation, data),
super::ImplSource::FnPointer(data) => confirm_fn_pointer_candidate(selcx, obligation, data),
super::ImplSource::DiscriminantKind(data) => {
@ -1905,6 +1908,48 @@ fn confirm_generator_candidate<'cx, 'tcx>(
.with_addl_obligations(obligations)
}
fn confirm_future_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
impl_source: ImplSourceFutureData<'tcx, PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
let gen_sig = impl_source.substs.as_generator().poly_sig();
let Normalized { value: gen_sig, obligations } = normalize_with_depth(
selcx,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
gen_sig,
);
debug!(?obligation, ?gen_sig, ?obligations, "confirm_future_candidate");
let tcx = selcx.tcx();
let fut_def_id = tcx.require_lang_item(LangItem::Future, None);
let predicate = super::util::future_trait_ref_and_outputs(
tcx,
fut_def_id,
obligation.predicate.self_ty(),
gen_sig,
)
.map_bound(|(trait_ref, return_ty)| {
debug_assert_eq!(tcx.associated_item(obligation.predicate.item_def_id).name, sym::Output);
ty::ProjectionPredicate {
projection_ty: ty::ProjectionTy {
substs: trait_ref.substs,
item_def_id: obligation.predicate.item_def_id,
},
term: return_ty.into(),
}
});
confirm_param_env_candidate(selcx, obligation, predicate, false)
.with_addl_obligations(impl_source.nested)
.with_addl_obligations(obligations)
}
fn confirm_discriminant_kind_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,

View file

@ -312,7 +312,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates);
}
self.assemble_generator_candidates(obligation, &mut candidates);
if lang_items.gen_trait() == Some(def_id) {
self.assemble_generator_candidates(obligation, &mut candidates);
} else if lang_items.future_trait() == Some(def_id) {
self.assemble_future_candidates(obligation, &mut candidates);
}
self.assemble_closure_candidates(obligation, &mut candidates);
self.assemble_fn_pointer_candidates(obligation, &mut candidates);
self.assemble_candidates_from_impls(obligation, &mut candidates);
@ -400,10 +405,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
if self.tcx().lang_items().gen_trait() != Some(obligation.predicate.def_id()) {
return;
}
// Okay to skip binder because the substs on generator types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
@ -422,6 +423,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
fn assemble_future_candidates(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let self_ty = obligation.self_ty().skip_binder();
if let ty::Generator(did, ..) = self_ty.kind() {
if let Some(rustc_hir::GeneratorKind::Async(_generator_kind)) =
self.tcx().generator_kind(did)
{
debug!(?self_ty, ?obligation, "assemble_future_candidates",);
candidates.vec.push(FutureCandidate);
}
}
}
/// Checks for the artificial impl that the compiler will create for an obligation like `X :
/// FnMut<..>` where `X` is a closure type.
///

View file

@ -23,10 +23,11 @@ use crate::traits::{
BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource,
ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData,
ImplSourceConstDestructData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData,
ImplSourceGeneratorData, ImplSourceObjectData, ImplSourcePointeeData, ImplSourceTraitAliasData,
ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, ObjectCastObligation,
Obligation, ObligationCause, OutputTypeParameterMismatch, PredicateObligation, Selection,
SelectionError, TraitNotObjectSafe, TraitObligation, Unimplemented, VtblSegment,
ImplSourceFutureData, ImplSourceGeneratorData, ImplSourceObjectData, ImplSourcePointeeData,
ImplSourceTraitAliasData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized,
ObjectCastObligation, Obligation, ObligationCause, OutputTypeParameterMismatch,
PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, TraitObligation,
Unimplemented, VtblSegment,
};
use super::BuiltinImplConditions;
@ -89,6 +90,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplSource::Generator(vtable_generator)
}
FutureCandidate => {
let vtable_future = self.confirm_future_candidate(obligation)?;
ImplSource::Future(vtable_future)
}
FnPointerCandidate { .. } => {
let data = self.confirm_fn_pointer_candidate(obligation)?;
ImplSource::FnPointer(data)
@ -685,7 +691,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?obligation, ?generator_def_id, ?substs, "confirm_generator_candidate");
let trait_ref = self.generator_trait_ref_unnormalized(obligation, substs);
let gen_sig = substs.as_generator().poly_sig();
// (1) Feels icky to skip the binder here, but OTOH we know
// that the self-type is an generator type and hence is
// in fact unparameterized (or at least does not reference any
// regions bound in the obligation). Still probably some
// refactoring could make this nicer.
let trait_ref = super::util::generator_trait_ref_and_outputs(
self.tcx(),
obligation.predicate.def_id(),
obligation.predicate.skip_binder().self_ty(), // (1)
gen_sig,
)
.map_bound(|(trait_ref, ..)| trait_ref);
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
debug!(?trait_ref, ?nested, "generator candidate obligations");
@ -693,6 +713,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(ImplSourceGeneratorData { generator_def_id, substs, nested })
}
fn confirm_future_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
) -> Result<ImplSourceFutureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
// Okay to skip binder because the substs on generator types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
let ty::Generator(generator_def_id, substs, _) = *self_ty.kind() else {
bug!("closure candidate for non-closure {:?}", obligation);
};
debug!(?obligation, ?generator_def_id, ?substs, "confirm_future_candidate");
let gen_sig = substs.as_generator().poly_sig();
let trait_ref = super::util::future_trait_ref_and_outputs(
self.tcx(),
obligation.predicate.def_id(),
obligation.predicate.no_bound_vars().expect("future has no bound vars").self_ty(),
gen_sig,
)
.map_bound(|(trait_ref, ..)| trait_ref);
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
debug!(?trait_ref, ?nested, "future candidate obligations");
Ok(ImplSourceFutureData { generator_def_id, substs, nested })
}
#[instrument(skip(self), level = "debug")]
fn confirm_closure_candidate(
&mut self,

View file

@ -1139,9 +1139,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ProjectionCandidate(_, ty::BoundConstness::ConstIfConst) => {}
// auto trait impl
AutoImplCandidate => {}
// generator, this will raise error in other places
// generator / future, this will raise error in other places
// or ignore error with const_async_blocks feature
GeneratorCandidate => {}
FutureCandidate => {}
// FnDef where the function is const
FnPointerCandidate { is_const: true } => {}
ConstDestructCandidate(_) => {}
@ -1620,6 +1621,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplCandidate(..)
| ClosureCandidate
| GeneratorCandidate
| FutureCandidate
| FnPointerCandidate { .. }
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate
@ -1638,6 +1640,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplCandidate(_)
| ClosureCandidate
| GeneratorCandidate
| FutureCandidate
| FnPointerCandidate { .. }
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate
@ -1668,6 +1671,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplCandidate(..)
| ClosureCandidate
| GeneratorCandidate
| FutureCandidate
| FnPointerCandidate { .. }
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate
@ -1680,6 +1684,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplCandidate(..)
| ClosureCandidate
| GeneratorCandidate
| FutureCandidate
| FnPointerCandidate { .. }
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate
@ -1761,6 +1766,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplCandidate(_)
| ClosureCandidate
| GeneratorCandidate
| FutureCandidate
| FnPointerCandidate { .. }
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate
@ -1770,6 +1776,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplCandidate(_)
| ClosureCandidate
| GeneratorCandidate
| FutureCandidate
| FnPointerCandidate { .. }
| BuiltinObjectCandidate
| BuiltinUnsizeCandidate
@ -2279,28 +2286,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.map_bound(|(trait_ref, _)| trait_ref)
}
fn generator_trait_ref_unnormalized(
&mut self,
obligation: &TraitObligation<'tcx>,
substs: SubstsRef<'tcx>,
) -> ty::PolyTraitRef<'tcx> {
let gen_sig = substs.as_generator().poly_sig();
// (1) Feels icky to skip the binder here, but OTOH we know
// that the self-type is an generator type and hence is
// in fact unparameterized (or at least does not reference any
// regions bound in the obligation). Still probably some
// refactoring could make this nicer.
super::util::generator_trait_ref_and_outputs(
self.tcx(),
obligation.predicate.def_id(),
obligation.predicate.skip_binder().self_ty(), // (1)
gen_sig,
)
.map_bound(|(trait_ref, ..)| trait_ref)
}
/// Returns the obligations that are implied by instantiating an
/// impl or trait. The obligations are substituted and fully
/// normalized. This is used when confirming an impl or default

View file

@ -318,6 +318,17 @@ pub fn generator_trait_ref_and_outputs<'tcx>(
sig.map_bound(|sig| (trait_ref, sig.yield_ty, sig.return_ty))
}
pub fn future_trait_ref_and_outputs<'tcx>(
tcx: TyCtxt<'tcx>,
fn_trait_def_id: DefId,
self_ty: Ty<'tcx>,
sig: ty::PolyGenSig<'tcx>,
) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> {
debug_assert!(!self_ty.has_escaping_bound_vars());
let trait_ref = tcx.mk_trait_ref(fn_trait_def_id, [self_ty]);
sig.map_bound(|sig| (trait_ref, sig.return_ty))
}
pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool {
assoc_item.defaultness(tcx).is_final()
&& tcx.impl_defaultness(assoc_item.container_id(tcx)).is_final()

View file

@ -413,7 +413,11 @@ impl<'tcx> LowerInto<'tcx, Ty<'tcx>> for &chalk_ir::Ty<RustInterner<'tcx>> {
TyKind::Closure(closure, substitution) => {
ty::Closure(closure.0, substitution.lower_into(interner))
}
TyKind::Generator(..) => unimplemented!(),
TyKind::Generator(generator, substitution) => ty::Generator(
generator.0,
substitution.lower_into(interner),
ast::Movability::Static,
),
TyKind::GeneratorWitness(..) => unimplemented!(),
TyKind::Never => ty::Never,
TyKind::Tuple(_len, substitution) => {

View file

@ -202,6 +202,12 @@ fn resolve_associated_item<'tcx>(
)),
substs: generator_data.substs,
}),
traits::ImplSource::Future(future_data) => Some(Instance {
def: ty::InstanceDef::Item(ty::WithOptConstParam::unknown(
future_data.generator_def_id,
)),
substs: future_data.substs,
}),
traits::ImplSource::Closure(closure_data) => {
let trait_closure_kind = tcx.fn_trait_kind_from_lang_item(trait_id).unwrap();
Instance::resolve_closure(

View file

@ -9,12 +9,8 @@
//! [`await`]: ../../std/keyword.await.html
//! [async book]: https://rust-lang.github.io/async-book/
use crate::{
ops::{Generator, GeneratorState},
pin::Pin,
ptr::NonNull,
task::{Context, Poll},
};
use crate::ptr::NonNull;
use crate::task::Context;
mod future;
mod into_future;
@ -48,6 +44,7 @@ pub use poll_fn::{poll_fn, PollFn};
/// non-Send/Sync as well, and we don't want that.
///
/// It also simplifies the HIR lowering of `.await`.
#[cfg_attr(not(bootstrap), lang = "ResumeTy")]
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
#[derive(Debug, Copy, Clone)]
@ -64,15 +61,21 @@ unsafe impl Sync for ResumeTy {}
/// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give
/// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`).
// This is `const` to avoid extra errors after we recover from `const async fn`
#[lang = "from_generator"]
#[cfg_attr(bootstrap, lang = "from_generator")]
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
#[rustc_const_unstable(feature = "gen_future", issue = "50547")]
#[inline]
pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
where
T: Generator<ResumeTy, Yield = ()>,
T: crate::ops::Generator<ResumeTy, Yield = ()>,
{
use crate::{
ops::{Generator, GeneratorState},
pin::Pin,
task::Poll,
};
#[rustc_diagnostic_item = "gen_future"]
struct GenFuture<T: Generator<ResumeTy, Yield = ()>>(T);
@ -109,3 +112,11 @@ pub unsafe fn get_context<'a, 'b>(cx: ResumeTy) -> &'a mut Context<'b> {
// that fulfills all the requirements for a mutable reference.
unsafe { &mut *cx.0.as_ptr().cast() }
}
#[cfg_attr(not(bootstrap), lang = "identity_future")]
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
#[inline]
pub const fn identity_future<O, Fut: Future<Output = O>>(f: Fut) -> Fut {
f
}

View file

@ -9,6 +9,7 @@ use crate::task::Ready;
/// scheduled to receive a wakeup instead.
#[must_use = "this `Poll` may be a `Pending` variant, which should be handled"]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(not(bootstrap), lang = "Poll")]
#[stable(feature = "futures_api", since = "1.36.0")]
pub enum Poll<T> {
/// Represents that a value is immediately ready.

View file

@ -11,12 +11,14 @@ async fn async_fn_test() {
foo().await;
}
// NONMSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}",
// NONMSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[GEN_SCOPE:![0-9]*]],
// MSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::async_fn_test::async_fn_env$0>",
// NONMSVC: [[GEN_SCOPE:!.*]] = !DINamespace(name: "async_fn_test",
// CHECK: [[SUSPEND_STRUCT:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend0", scope: [[GEN]],
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__awaitee", scope: [[SUSPEND_STRUCT]], {{.*}}, baseType: [[AWAITEE_TYPE:![0-9]*]],
// NONMSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "GenFuture<async_fn_debug_awaitee_field::foo::{async_fn_env#0}>",
// MSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "GenFuture<enum2$<async_fn_debug_awaitee_field::foo::async_fn_env$0> >",
// NONMSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[AWAITEE_SCOPE:![0-9]*]],
// MSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::foo::async_fn_env$0>",
// NONMSVC: [[AWAITEE_SCOPE]] = !DINamespace(name: "foo",
fn main() {
let _fn = async_fn_test();

View file

@ -72,7 +72,7 @@
67| | }
68| 2| }
------------------
| async2::executor::block_on::<core::future::from_generator::GenFuture<async2::async_func::{closure#0}>>:
| async2::executor::block_on::<async2::async_func::{closure#0}>:
| 51| 1| pub fn block_on<F: Future>(mut future: F) -> F::Output {
| 52| 1| let mut future = unsafe { Pin::new_unchecked(&mut future) };
| 53| 1| use std::hint::unreachable_unchecked;
@ -92,7 +92,7 @@
| 67| | }
| 68| 1| }
------------------
| async2::executor::block_on::<core::future::from_generator::GenFuture<async2::async_func_just_println::{closure#0}>>:
| async2::executor::block_on::<async2::async_func_just_println::{closure#0}>:
| 51| 1| pub fn block_on<F: Future>(mut future: F) -> F::Output {
| 52| 1| let mut future = unsafe { Pin::new_unchecked(&mut future) };
| 53| 1| use std::hint::unreachable_unchecked;

View file

@ -1,8 +1,7 @@
error[E0267]: `break` inside of an `async` block
--> $DIR/async-block-control-flow-static-semantics.rs:32:9
|
LL | async {
| ___________-
LL | / async {
LL | | break 0u8;
| | ^^^^^^^^^ cannot `break` inside of an `async` block
LL | | };
@ -11,8 +10,7 @@ LL | | };
error[E0267]: `break` inside of an `async` block
--> $DIR/async-block-control-flow-static-semantics.rs:39:13
|
LL | async {
| _______________-
LL | / async {
LL | | break 0u8;
| | ^^^^^^^^^ cannot `break` inside of an `async` block
LL | | };

View file

@ -1,11 +1,11 @@
error[E0373]: async block may outlive the current function, but it borrows `x`, which is owned by the current function
--> $DIR/async-borrowck-escaping-block-error.rs:6:20
--> $DIR/async-borrowck-escaping-block-error.rs:6:14
|
LL | Box::new(async { x } )
| ^^-^^
| | |
| | `x` is borrowed here
| may outlive borrowed value `x`
| ^^^^^^^^-^^
| | |
| | `x` is borrowed here
| may outlive borrowed value `x`
|
note: async block is returned here
--> $DIR/async-borrowck-escaping-block-error.rs:6:5
@ -18,13 +18,13 @@ LL | Box::new(async move { x } )
| ++++
error[E0373]: async block may outlive the current function, but it borrows `x`, which is owned by the current function
--> $DIR/async-borrowck-escaping-block-error.rs:11:11
--> $DIR/async-borrowck-escaping-block-error.rs:11:5
|
LL | async { *x }
| ^^--^^
| | |
| | `x` is borrowed here
| may outlive borrowed value `x`
| ^^^^^^^^--^^
| | |
| | `x` is borrowed here
| may outlive borrowed value `x`
|
note: async block is returned here
--> $DIR/async-borrowck-escaping-block-error.rs:11:5

View file

@ -1,20 +1,20 @@
error[E0308]: mismatched types
--> $DIR/generator-desc.rs:10:25
--> $DIR/generator-desc.rs:10:19
|
LL | fun(async {}, async {});
| -- ^^
| | |
| | expected `async` block, found a different `async` block
| | arguments to this function are incorrect
| the expected `async` block
| -------- ^^^^^^^^
| | |
| | expected `async` block, found a different `async` block
| | arguments to this function are incorrect
| the expected `async` block
|
= note: expected `async` block `[static generator@$DIR/generator-desc.rs:10:15: 10:17]`
found `async` block `[static generator@$DIR/generator-desc.rs:10:25: 10:27]`
= note: expected `async` block `impl Future<Output = ()>` (`async` block)
found `async` block `impl Future<Output = ()>` (`async` block)
note: function defined here
--> $SRC_DIR/core/src/future/mod.rs:LL:COL
|
LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
| ^^^^^^^^^^^^^^
LL | pub const fn identity_future<O, Fut: Future<Output = O>>(f: Fut) -> Fut {
| ^^^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/generator-desc.rs:12:16
@ -53,16 +53,8 @@ LL | fun((async || {})(), (async || {})());
| | the expected `async` closure body
| arguments to this function are incorrect
|
::: $SRC_DIR/core/src/future/mod.rs:LL:COL
|
LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
| -------------------------------
| |
| the expected opaque type
| the found opaque type
|
= note: expected opaque type `impl Future<Output = ()>` (`async` closure body)
found opaque type `impl Future<Output = ()>` (`async` closure body)
= note: expected `async` closure body `impl Future<Output = ()>` (`async` closure body)
found `async` closure body `impl Future<Output = ()>` (`async` closure body)
note: function defined here
--> $DIR/generator-desc.rs:8:4
|

View file

@ -59,10 +59,10 @@ LL | fn make_non_send_future2() -> impl Future<Output = Arc<RefCell<i32>>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: required because it captures the following types: `ResumeTy`, `impl Future<Output = Arc<RefCell<i32>>>`, `()`, `Ready<i32>`
note: required because it's used within this `async` block
--> $DIR/issue-68112.rs:60:26
--> $DIR/issue-68112.rs:60:20
|
LL | let send_fut = async {
| __________________________^
| ____________________^
LL | | let non_send_fut = make_non_send_future2();
LL | | let _ = non_send_fut.await;
LL | | ready(0).await;

View file

@ -59,10 +59,10 @@ LL | fn make_non_send_future2() -> impl Future<Output = Arc<RefCell<i32>>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: required because it captures the following types: `ResumeTy`, `impl Future<Output = Arc<RefCell<i32>>>`, `()`, `i32`, `Ready<i32>`
note: required because it's used within this `async` block
--> $DIR/issue-68112.rs:60:26
--> $DIR/issue-68112.rs:60:20
|
LL | let send_fut = async {
| __________________________^
| ____________________^
LL | | let non_send_fut = make_non_send_future2();
LL | | let _ = non_send_fut.await;
LL | | ready(0).await;

View file

@ -20,10 +20,9 @@ LL | | }
| |_^
= note: required because it captures the following types: `ResumeTy`, `impl Future<Output = ()>`, `()`
note: required because it's used within this `async` block
--> $DIR/issue-70935-complex-spans.rs:16:16
--> $DIR/issue-70935-complex-spans.rs:16:5
|
LL | async move {
| ________________^
LL | / async move {
LL | | baz(|| async{
LL | | foo(tx.clone());
LL | | }).await;

View file

@ -1,8 +1,8 @@
error[E0373]: async block may outlive the current function, but it borrows `room_ref`, which is owned by the current function
--> $DIR/issue-78938-async-block.rs:8:39
--> $DIR/issue-78938-async-block.rs:8:33
|
LL | let gameloop_handle = spawn(async {
| _______________________________________^
| _________________________________^
LL | | game_loop(Arc::clone(&room_ref))
| | -------- `room_ref` is borrowed here
LL | | });

View file

@ -1,8 +1,7 @@
error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> $DIR/try-on-option-in-async.rs:8:10
|
LL | async {
| ___________-
LL | / async {
LL | | let x: Option<u32> = None;
LL | | x?;
| | ^ cannot use the `?` operator in an async block that returns `{integer}`

View file

@ -1,32 +1,47 @@
error[E0277]: the trait bound `[static generator@$DIR/async.rs:7:29: 9:2]: Generator<ResumeTy>` is not satisfied
error[E0277]: `impl Future<Output = u32>` is not a future
--> $DIR/async.rs:7:29
|
LL | async fn foo(x: u32) -> u32 {
| _____________________________^
| _____________________________-
LL | | x
LL | | }
| |_^ the trait `Generator<ResumeTy>` is not implemented for `[static generator@$DIR/async.rs:7:29: 9:2]`
| | ^
| | |
| |_`impl Future<Output = u32>` is not a future
| required by a bound introduced by this call
|
note: required by a bound in `std::future::from_generator`
= help: the trait `Future` is not implemented for `impl Future<Output = u32>`
= note: impl Future<Output = u32> must be a future or must implement `IntoFuture` to be awaited
note: required by a bound in `identity_future`
--> $SRC_DIR/core/src/future/mod.rs:LL:COL
|
LL | T: Generator<ResumeTy, Yield = ()>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `std::future::from_generator`
LL | pub const fn identity_future<O, Fut: Future<Output = O>>(f: Fut) -> Fut {
| ^^^^^^^^^^^^^^^^^^ required by this bound in `identity_future`
error[E0280]: the requirement `<[static generator@$DIR/async.rs:7:29: 9:2] as Generator<ResumeTy>>::Yield == ()` is not satisfied
error[E0277]: the size for values of type `<impl Future<Output = u32> as Future>::Output` cannot be known at compilation time
--> $DIR/async.rs:7:29
|
LL | async fn foo(x: u32) -> u32 {
| _____________________________^
LL | | x
LL | | }
| |_^
| |_^ doesn't have a size known at compile-time
|
note: required by a bound in `std::future::from_generator`
= help: the trait `Sized` is not implemented for `<impl Future<Output = u32> as Future>::Output`
note: required by a bound in `identity_future`
--> $SRC_DIR/core/src/future/mod.rs:LL:COL
|
LL | T: Generator<ResumeTy, Yield = ()>,
| ^^^^^^^^^^ required by this bound in `std::future::from_generator`
LL | pub const fn identity_future<O, Fut: Future<Output = O>>(f: Fut) -> Fut {
| ^ required by this bound in `identity_future`
error[E0277]: `impl Future<Output = u32>` is not a future
--> $DIR/async.rs:7:25
|
LL | async fn foo(x: u32) -> u32 {
| ^^^ `impl Future<Output = u32>` is not a future
|
= help: the trait `Future` is not implemented for `impl Future<Output = u32>`
= note: impl Future<Output = u32> must be a future or must implement `IntoFuture` to be awaited
error[E0280]: the requirement `<impl Future<Output = u32> as Future>::Output == u32` is not satisfied
--> $DIR/async.rs:7:25
@ -34,6 +49,6 @@ error[E0280]: the requirement `<impl Future<Output = u32> as Future>::Output ==
LL | async fn foo(x: u32) -> u32 {
| ^^^
error: aborting due to 3 previous errors
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -1,6 +1,6 @@
// check-pass
#![feature(gen_future, generator_trait, negative_impls)]
#![feature(generator_trait, negative_impls)]
use std::ops::{Generator, GeneratorState};
use std::task::{Poll, Context};

View file

@ -78,20 +78,21 @@ LL | impl<P: Deref<Target: Unpin>> Pin<P> {
error[E0308]: mismatched types
--> $DIR/expected-boxed-future-isnt-pinned.rs:28:5
|
LL | fn zap() -> BoxFuture<'static, i32> {
| ----------------------- expected `Pin<Box<(dyn Future<Output = i32> + Send + 'static)>>` because of return type
LL | / async {
LL | | 42
LL | | }
| |_____^ expected struct `Pin`, found opaque type
| | ^
| | |
| |_____expected struct `Pin`, found `async` block
| arguments to this function are incorrect
|
::: $SRC_DIR/core/src/future/mod.rs:LL:COL
= note: expected struct `Pin<Box<dyn Future<Output = i32> + Send>>`
found `async` block `impl Future<Output = {integer}>`
note: function defined here
--> $SRC_DIR/core/src/future/mod.rs:LL:COL
|
LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
| ------------------------------- the found opaque type
|
= note: expected struct `Pin<Box<(dyn Future<Output = i32> + Send + 'static)>>`
found opaque type `impl Future<Output = {integer}>`
LL | pub const fn identity_future<O, Fut: Future<Output = O>>(f: Fut) -> Fut {
| ^^^^^^^^^^^^^^^
help: you need to pin and box this expression
|
LL ~ Box::pin(async {

View file

@ -427,9 +427,7 @@ fn lint_for_missing_headers(
let body = cx.tcx.hir().body(body_id);
let ret_ty = typeck.expr_ty(body.value);
if implements_trait(cx, ret_ty, future, &[]);
if let ty::Opaque(_, subs) = ret_ty.kind();
if let Some(gen) = subs.types().next();
if let ty::Generator(_, subs, _) = gen.kind();
if let ty::Generator(_, subs, _) = ret_ty.kind();
if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::Result);
then {
span_lint(

View file

@ -177,7 +177,7 @@ fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>)
if let Some(args) = cx
.tcx
.lang_items()
.from_generator_fn()
.identity_future_fn()
.and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id));
if args.len() == 1;
if let Expr {

View file

@ -45,7 +45,7 @@ if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kin
&& expr1 = &cx.tcx.hir().body(body_id).value
&& let ExprKind::Call(func, args) = expr1.kind
&& let ExprKind::Path(ref qpath) = func.kind
&& matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _))
&& matches!(qpath, QPath::LangItem(LangItem::IdentityFuture, _))
&& args.len() == 1
&& let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind
&& let FnRetTy::DefaultReturn(_) = fn_decl1.output