Skip to content

Commit

Permalink
Auto merge of #127827 - compiler-errors:async-closure-closure-async, …
Browse files Browse the repository at this point in the history
…r=<try>

[EXPERIMENT] Rewrite closure-of-async to async-closure

to see what the fallout is, to gauge how forwards-compatible `async || {}` is w/ `|| async {}`.

uwuwuwu
  • Loading branch information
bors committed Aug 9, 2024
2 parents 899eb03 + ce86e83 commit 71e0d7c
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 66 deletions.
31 changes: 28 additions & 3 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,30 @@ impl Expr {
}
}

pub fn peel_uwu(&self) -> &Expr {
let mut expr = self;
loop {
match &expr.kind {
ExprKind::Block(blk, None) => {
if blk.stmts.len() == 1
&& let StmtKind::Expr(blk) = &blk.stmts[0].kind
{
expr = blk;
} else {
break;
}
}
ExprKind::Paren(paren) => {
expr = paren;
}
_ => {
break;
}
}
}
expr
}

pub fn peel_parens(&self) -> &Expr {
let mut expr = self;
while let ExprKind::Paren(inner) = &expr.kind {
Expand Down Expand Up @@ -1614,15 +1638,16 @@ pub struct QSelf {
}

/// A capture clause used in closures and `async` blocks.
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
#[derive(Clone, Copy, PartialEq, Ord, Eq, PartialOrd, Debug)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum CaptureBy {
/// `move` keyword was not specified.
Ref,
/// `move |x| y + x`.
Value {
/// The span of the `move` keyword.
move_kw: Span,
},
/// `move` keyword was not specified.
Ref,
}

/// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`.
Expand Down
71 changes: 59 additions & 12 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,18 +219,65 @@ impl<'hir> LoweringContext<'_, 'hir> {
*fn_decl_span,
*fn_arg_span,
),
None => self.lower_expr_closure(
binder,
*capture_clause,
e.id,
hir_id,
*constness,
*movability,
fn_decl,
body,
*fn_decl_span,
*fn_arg_span,
),
None => {
let peeled = body.peel_uwu();
if let ast::ExprKind::Gen(
_,
block,
gen_kind @ ast::GenBlockKind::Async,
span,
) = &peeled.kind
{
let coroutine_kind = match gen_kind {
GenBlockKind::Async => CoroutineKind::Async {
span: *span,
closure_id: peeled.node_id(),
return_impl_trait_id: self.next_node_id(),
},
GenBlockKind::Gen => CoroutineKind::Gen {
span: *span,
closure_id: peeled.node_id(),
return_impl_trait_id: self.next_node_id(),
},
GenBlockKind::AsyncGen => CoroutineKind::AsyncGen {
span: *span,
closure_id: peeled.node_id(),
return_impl_trait_id: self.next_node_id(),
},
};
let id = self.next_node_id();
self.lower_expr_coroutine_closure(
binder,
*capture_clause,
e.id,
hir_id,
coroutine_kind,
fn_decl,
&ast::Expr {
id,
span: *span,
kind: ExprKind::Block(block.clone(), None),
attrs: thin_vec![],
tokens: None,
},
*fn_decl_span,
*fn_arg_span,
)
} else {
self.lower_expr_closure(
binder,
*capture_clause,
e.id,
hir_id,
*constness,
*movability,
fn_decl,
body,
*fn_decl_span,
*fn_arg_span,
)
}
}
},
ExprKind::Gen(capture_clause, block, genblock_kind, decl_span) => {
let desugaring_kind = match genblock_kind {
Expand Down
24 changes: 13 additions & 11 deletions compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_middle::span_bug;
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
use rustc_middle::ty::{self, GenericArgs, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::error_reporting::traits::ArgKind;
use rustc_trait_selection::traits;
Expand Down Expand Up @@ -563,25 +563,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
};

let mut return_ty = None;

// FIXME: We may want to elaborate here, though I assume this will be exceedingly rare.
for bound in self.obligations_for_self_ty(return_vid) {
if let Some(ret_projection) = bound.predicate.as_projection_clause()
&& let Some(ret_projection) = ret_projection.no_bound_vars()
&& self.tcx.is_lang_item(ret_projection.def_id(), LangItem::FutureOutput)
{
let sig = projection.rebind(self.tcx.mk_fn_sig(
input_tys,
ret_projection.term.expect_type(),
false,
hir::Safety::Safe,
Abi::Rust,
));

return Some(ExpectedSig { cause_span, sig });
return_ty = Some(ret_projection.term.expect_type());
}
}

None
let sig = projection.rebind(self.tcx.mk_fn_sig(
input_tys,
return_ty.unwrap_or_else(|| self.next_ty_var(cause_span.unwrap_or(DUMMY_SP))),
false,
hir::Safety::Safe,
Abi::Rust,
));

return Some(ExpectedSig { cause_span, sig });
}

fn sig_of_closure(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ LL | outlives::<'a>(call_once(c));
LL |
LL | let c = async move || { println!("{}", *x.0); };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `x` occurs here
|
help: consider cloning the value if the performance cost is acceptable
|
LL | let c = async || { println!("{}", *x.0); }.clone();
| ++++++++

error[E0597]: `c` does not live long enough
--> $DIR/without-precise-captures-we-are-powerless.rs:33:20
Expand Down
26 changes: 2 additions & 24 deletions tests/ui/async-await/async-closures/wrong-fn-kind.stderr
Original file line number Diff line number Diff line change
@@ -1,24 +1,3 @@
error[E0525]: expected a closure that implements the `async Fn` trait, but this closure only implements `async FnOnce`
--> $DIR/wrong-fn-kind.rs:17:20
|
LL | needs_async_fn(move || async move {
| -------------- -^^^^^^
| | |
| _____|______________this closure implements `async FnOnce`, not `async Fn`
| | |
| | required by a bound introduced by this call
LL | |
LL | | println!("{x}");
| | - closure is `async FnOnce` because it moves the variable `x` out of its environment
LL | | });
| |_____- the requirement to implement `async Fn` derives from here
|
note: required by a bound in `needs_async_fn`
--> $DIR/wrong-fn-kind.rs:5:27
|
LL | fn needs_async_fn(_: impl async Fn()) {}
| ^^^^^^^^^^ required by this bound in `needs_async_fn`

error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
--> $DIR/wrong-fn-kind.rs:9:20
|
Expand All @@ -35,7 +14,6 @@ LL |
LL | x += 1;
| - mutable borrow occurs due to use of `x` in closure

error: aborting due to 2 previous errors
error: aborting due to 1 previous error

Some errors have detailed explanations: E0525, E0596.
For more information about an error, try `rustc --explain E0525`.
For more information about this error, try `rustc --explain E0596`.
25 changes: 11 additions & 14 deletions tests/ui/async-await/issue-69446-fnmut-capture.stderr
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
error: captured variable cannot escape `FnMut` closure body
--> $DIR/issue-69446-fnmut-capture.rs:19:17
error: async closure does not implement `FnMut` because it captures state from its environment
--> $DIR/issue-69446-fnmut-capture.rs:19:9
|
LL | let mut x = Foo;
| ----- variable defined here
LL | bar(move || async {
| _______________-_^
| | |
| | inferred to be a `FnMut` closure
LL | | x.foo();
| | - variable captured here
LL | | });
| |_____^ returns an `async` block that contains a reference to a captured variable, which then escapes the closure body
LL | bar(move || async {
| --- ^^^^^^^
| |
| required by a bound introduced by this call
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
note: required by a bound in `bar`
--> $DIR/issue-69446-fnmut-capture.rs:12:25
|
LL | async fn bar<T>(_: impl FnMut() -> T)
| ^^^^^^^^^^^^ required by this bound in `bar`

error: aborting due to 1 previous error

2 changes: 2 additions & 0 deletions tests/ui/async-await/issue-70935-complex-spans.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ note: required because it appears within the type `NotSync`
LL | struct NotSync(PhantomData<*mut ()>);
| ^^^^^^^
= note: required for `&NotSync` to implement `Send`
= note: required because it appears within the type `(&NotSync,)`
note: required because it's used within this closure
--> $DIR/issue-70935-complex-spans.rs:19:13
|
Expand Down Expand Up @@ -46,6 +47,7 @@ note: required because it appears within the type `NotSync`
LL | struct NotSync(PhantomData<*mut ()>);
| ^^^^^^^
= note: required for `&NotSync` to implement `Send`
= note: required because it appears within the type `(&NotSync,)`
note: required because it's used within this closure
--> $DIR/issue-70935-complex-spans.rs:19:13
|
Expand Down
11 changes: 9 additions & 2 deletions tests/ui/impl-trait/opaque-cast-field-access-in-future.stderr
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
error[E0282]: type annotations needed
--> $DIR/opaque-cast-field-access-in-future.rs:7:14
|
LL | &mut foo.bar;
| ^^^ cannot infer type

error[E0283]: type annotations needed
--> $DIR/opaque-cast-field-access-in-future.rs:22:17
|
Expand All @@ -6,6 +12,7 @@ LL | fn run() -> Foo<impl Future<Output = ()>> {
|
= note: cannot satisfy `_: Future`

error: aborting due to 1 previous error
error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0283`.
Some errors have detailed explanations: E0282, E0283.
For more information about an error, try `rustc --explain E0282`.

0 comments on commit 71e0d7c

Please sign in to comment.