Skip to content

Commit c0d88d5

Browse files
Add some tests
1 parent de528b1 commit c0d88d5

24 files changed

+402
-9
lines changed

compiler/rustc_hir_typeck/src/upvar.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -394,13 +394,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
394394
coroutine_captures_by_ref_ty,
395395
);
396396

397-
let ty::Coroutine(_, args) = *self.typeck_results.borrow().expr_ty(body.value).kind()
397+
let ty::Coroutine(_, coroutine_args) =
398+
*self.typeck_results.borrow().expr_ty(body.value).kind()
398399
else {
399400
bug!();
400401
};
401402
self.demand_eqtype(
402403
span,
403-
args.as_coroutine().kind_ty(),
404+
coroutine_args.as_coroutine().kind_ty(),
404405
Ty::from_closure_kind(self.tcx, closure_kind),
405406
);
406407
}

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
371371
obligation: &PolyTraitObligation<'tcx>,
372372
candidates: &mut SelectionCandidateSet<'tcx>,
373373
) {
374-
if let Some(closure_kind) = obligation.self_ty().skip_binder().to_opt_closure_kind()
375-
&& let Some(goal_kind) =
376-
obligation.predicate.skip_binder().trait_ref.args.type_at(1).to_opt_closure_kind()
374+
let self_ty = obligation.self_ty().skip_binder();
375+
let target_kind_ty = obligation.predicate.skip_binder().trait_ref.args.type_at(1);
376+
377+
// `to_opt_closure_kind` is kind of ICEy when it sees non-int types.
378+
if !(self_ty.is_integral() || self_ty.is_ty_var()) {
379+
return;
380+
}
381+
if !(target_kind_ty.is_integral() || self_ty.is_ty_var()) {
382+
return;
383+
}
384+
385+
if let Some(closure_kind) = self_ty.to_opt_closure_kind()
386+
&& let Some(goal_kind) = target_kind_ty.to_opt_closure_kind()
377387
{
378388
if closure_kind.extends(goal_kind) {
379389
candidates.vec.push(AsyncFnKindHelperCandidate);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// aux-build:block-on.rs
2+
// edition:2021
3+
4+
#![feature(async_closure)]
5+
6+
extern crate block_on;
7+
8+
fn main() {
9+
block_on::block_on(async {
10+
let c = async |x| {};
11+
c(1i32).await;
12+
c(2usize).await;
13+
//~^ ERROR mismatched types
14+
});
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/arg-mismatch.rs:12:11
3+
|
4+
LL | c(2usize).await;
5+
| - ^^^^^^ expected `i32`, found `usize`
6+
| |
7+
| arguments to this function are incorrect
8+
|
9+
note: closure parameter defined here
10+
--> $DIR/arg-mismatch.rs:10:24
11+
|
12+
LL | let c = async |x| {};
13+
| ^
14+
help: change the type of the numeric literal from `usize` to `i32`
15+
|
16+
LL | c(2i32).await;
17+
| ~~~
18+
19+
error: aborting due to 1 previous error
20+
21+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// aux-build:block-on.rs
2+
// edition:2021
3+
// run-pass
4+
5+
// FIXME(async_closures): When `fn_sig_for_fn_abi` is fixed, remove this.
6+
// ignore-pass (test emits codegen-time warnings)
7+
8+
#![feature(async_closure, async_fn_traits)]
9+
10+
extern crate block_on;
11+
12+
use std::ops::AsyncFnOnce;
13+
14+
fn main() {
15+
block_on::block_on(async {
16+
let x = async || {};
17+
18+
async fn needs_async_fn_once(x: impl AsyncFnOnce()) {
19+
x().await;
20+
}
21+
needs_async_fn_once(x).await;
22+
});
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
WARN rustc_codegen_ssa::mir::locals Unexpected initial operand type: expected std::pin::Pin<&ReErased mut Coroutine(DefId(0:8 ~ async_fn_once_for_async_fn[6cdf]::main::{closure#0}::{closure#1}), [i32, std::future::ResumeTy, (), (), CoroutineWitness(DefId(0:8 ~ async_fn_once_for_async_fn[6cdf]::main::{closure#0}::{closure#1}), []), ()])>, found std::pin::Pin<&ReErased mut Coroutine(DefId(0:8 ~ async_fn_once_for_async_fn[6cdf]::main::{closure#0}::{closure#1}), [i8, std::future::ResumeTy, (), (), CoroutineWitness(DefId(0:8 ~ async_fn_once_for_async_fn[6cdf]::main::{closure#0}::{closure#1}), []), ()])>.See <https://github.com/rust-lang/rust/issues/114858>.
2+
WARN rustc_codegen_ssa::mir::locals Unexpected initial operand type: expected *mut Coroutine(DefId(0:8 ~ async_fn_once_for_async_fn[6cdf]::main::{closure#0}::{closure#1}), [i8, std::future::ResumeTy, (), (), CoroutineWitness(DefId(0:8 ~ async_fn_once_for_async_fn[6cdf]::main::{closure#0}::{closure#1}), []), ()]), found *mut Coroutine(DefId(0:8 ~ async_fn_once_for_async_fn[6cdf]::main::{closure#0}::{closure#1}), [i32, std::future::ResumeTy, (), (), CoroutineWitness(DefId(0:8 ~ async_fn_once_for_async_fn[6cdf]::main::{closure#0}::{closure#1}), []), ()]).See <https://github.com/rust-lang/rust/issues/114858>.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// edition: 2021
2+
3+
#![feature(async_closure, noop_waker, async_fn_traits)]
4+
5+
use std::future::Future;
6+
use std::pin::pin;
7+
use std::task::*;
8+
9+
pub fn block_on<T>(fut: impl Future<Output = T>) -> T {
10+
let mut fut = pin!(fut);
11+
// Poll loop, just to test the future...
12+
let ctx = &mut Context::from_waker(Waker::noop());
13+
14+
loop {
15+
match unsafe { fut.as_mut().poll(ctx) } {
16+
Poll::Pending => {}
17+
Poll::Ready(t) => break t,
18+
}
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// aux-build:block-on.rs
2+
// edition:2021
3+
// run-pass
4+
5+
#![feature(async_closure)]
6+
7+
extern crate block_on;
8+
9+
fn main() {
10+
block_on::block_on(async {
11+
let x = async |x: &str| -> String { x.to_owned() };
12+
let mut s = x("hello, world").await;
13+
s.truncate(4);
14+
println!("{s}");
15+
});
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// aux-build:block-on.rs
2+
// edition:2021
3+
// build-pass
4+
5+
#![feature(async_closure, async_fn_traits)]
6+
7+
extern crate block_on;
8+
9+
use std::future::Future;
10+
use std::marker::PhantomData;
11+
use std::ops::AsyncFn;
12+
13+
struct S;
14+
struct B<'b>(PhantomData<&'b mut &'b mut ()>);
15+
16+
impl S {
17+
async fn q<F: AsyncFn(B<'_>)>(self, f: F) {
18+
f(B(PhantomData)).await;
19+
}
20+
}
21+
22+
fn main() {
23+
block_on::block_on(async {
24+
S.q(async |b: B<'_>| { println!("...") }).await;
25+
});
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// aux-build:block-on.rs
2+
// edition:2021
3+
4+
// known-bug: unknown
5+
// Borrow checking doesn't like that higher-ranked output...
6+
7+
#![feature(async_closure)]
8+
9+
extern crate block_on;
10+
11+
fn main() {
12+
block_on::block_on(async {
13+
let x = async move |x: &str| -> &str {
14+
x
15+
};
16+
let s = x("hello!").await;
17+
});
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/higher-ranked-return.rs:13:46
3+
|
4+
LL | let x = async move |x: &str| -> &str {
5+
| ________________________________-________----_^
6+
| | | |
7+
| | | return type of async closure `{async closure body@$DIR/higher-ranked-return.rs:13:46: 15:10}` contains a lifetime `'2`
8+
| | let's call the lifetime of this reference `'1`
9+
LL | | x
10+
LL | | };
11+
| |_________^ returning this value requires that `'1` must outlive `'2`
12+
13+
error: aborting due to 1 previous error
14+
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
// aux-build:block-on.rs
12
// edition:2021
2-
// check-pass
3+
// build-pass
34

45
#![feature(async_closure)]
56

7+
extern crate block_on;
8+
69
fn main() {
7-
let x = async move |x: &str| {
8-
println!("{x}");
9-
};
10+
block_on::block_on(async {
11+
let x = async move |x: &str| {
12+
println!("{x}");
13+
};
14+
x("hello!").await;
15+
});
1016
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// edition:2021
2+
3+
#![feature(async_closure)]
4+
5+
fn main() {
6+
fn needs_fn(x: impl FnOnce()) {}
7+
needs_fn(async || {});
8+
//~^ ERROR expected a `FnOnce()` closure, found `{coroutine-closure@
9+
// FIXME(async_closures): This should explain in more detail how async fns don't
10+
// implement the regular `Fn` traits. Or maybe we should just fix it and make them
11+
// when there are no upvars or whatever.
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0277]: expected a `FnOnce()` closure, found `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}`
2+
--> $DIR/is-not-fn.rs:7:14
3+
|
4+
LL | needs_fn(async || {});
5+
| -------- ^^^^^^^^^^^ expected an `FnOnce()` closure, found `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the trait `FnOnce<()>` is not implemented for `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}`
10+
= note: wrap the `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}` in a closure with no arguments: `|| { /* code */ }`
11+
note: required by a bound in `needs_fn`
12+
--> $DIR/is-not-fn.rs:6:25
13+
|
14+
LL | fn needs_fn(x: impl FnOnce()) {}
15+
| ^^^^^^^^ required by this bound in `needs_fn`
16+
17+
error: aborting due to 1 previous error
18+
19+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// aux-build:block-on.rs
2+
// edition:2021
3+
4+
#![feature(async_closure)]
5+
6+
extern crate block_on;
7+
8+
struct NoCopy;
9+
10+
fn main() {
11+
block_on::block_on(async {
12+
let s = NoCopy;
13+
let x = async move || {
14+
drop(s);
15+
};
16+
x().await;
17+
x().await;
18+
//~^ ERROR use of moved value: `x`
19+
});
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0382]: use of moved value: `x`
2+
--> $DIR/move-consuming-capture.rs:17:9
3+
|
4+
LL | let x = async move || {
5+
| - move occurs because `x` has type `{coroutine-closure@$DIR/move-consuming-capture.rs:13:17: 13:30}`, which does not implement the `Copy` trait
6+
...
7+
LL | x().await;
8+
| --- `x` moved due to this method call
9+
LL | x().await;
10+
| ^ value used here after move
11+
|
12+
note: `async_call_once` takes ownership of the receiver `self`, which moves `x`
13+
--> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0382`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// aux-build:block-on.rs
2+
// edition:2021
3+
// build-pass
4+
5+
#![feature(async_closure)]
6+
7+
extern crate block_on;
8+
9+
fn main() {
10+
block_on::block_on(async {
11+
let s = String::from("hello, world");
12+
let c = async move || {
13+
println!("{s}");
14+
};
15+
c().await;
16+
c().await;
17+
18+
fn is_static<T: 'static>(_: T) {}
19+
is_static(c);
20+
});
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// aux-build:block-on.rs
2+
// edition:2021
3+
// run-pass
4+
5+
#![feature(async_closure)]
6+
7+
extern crate block_on;
8+
9+
fn main() {
10+
block_on::block_on(async {
11+
let mut prefix = String::from("Hello");
12+
let mut c = async move |x: &str| {
13+
prefix.push(',');
14+
println!("{prefix} {x}!")
15+
};
16+
c("world").await;
17+
c("rust").await;
18+
});
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// aux-build:block-on.rs
2+
// edition:2021
3+
4+
#![feature(async_closure)]
5+
6+
extern crate block_on;
7+
8+
// Make sure that we can't make an async closure that evaluates to a self-borrow.
9+
// i.e. that the generator may reference captures, but the future's return type can't.
10+
11+
fn main() {
12+
block_on::block_on(async {
13+
let s = String::new();
14+
let x = async move || -> &String { &s };
15+
//~^ ERROR lifetime may not live long enough
16+
17+
let s = String::new();
18+
let x = async move || { &s };
19+
//~^ ERROR lifetime may not live long enough
20+
});
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/not-lending.rs:14:42
3+
|
4+
LL | let x = async move || -> &String { &s };
5+
| ------------------------ ^^^^^^ returning this value requires that `'1` must outlive `'2`
6+
| | |
7+
| | return type of async closure `{async closure body@$DIR/not-lending.rs:14:42: 14:48}` contains a lifetime `'2`
8+
| lifetime `'1` represents this closure's body
9+
|
10+
= note: closure implements `Fn`, so references to captured variables can't escape the closure
11+
12+
error: lifetime may not live long enough
13+
--> $DIR/not-lending.rs:18:31
14+
|
15+
LL | let x = async move || { &s };
16+
| ------------- ^^^^^^ returning this value requires that `'1` must outlive `'2`
17+
| | |
18+
| | return type of async closure `{async closure body@$DIR/not-lending.rs:18:31: 18:37}` contains a lifetime `'2`
19+
| lifetime `'1` represents this closure's body
20+
|
21+
= note: closure implements `Fn`, so references to captured variables can't escape the closure
22+
23+
error: aborting due to 2 previous errors
24+

0 commit comments

Comments
 (0)