Skip to content

Commit

Permalink
Add some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Jan 26, 2024
1 parent fe2b33f commit ca6c57a
Show file tree
Hide file tree
Showing 24 changed files with 399 additions and 9 deletions.
5 changes: 3 additions & 2 deletions compiler/rustc_hir_typeck/src/upvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,13 +394,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
coroutine_captures_by_ref_ty,
);

let ty::Coroutine(_, args) = *self.typeck_results.borrow().expr_ty(body.value).kind()
let ty::Coroutine(_, coroutine_args) =
*self.typeck_results.borrow().expr_ty(body.value).kind()
else {
bug!();
};
self.demand_eqtype(
span,
args.as_coroutine().kind_ty(),
coroutine_args.as_coroutine().kind_ty(),
Ty::from_closure_kind(self.tcx, closure_kind),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
if let Some(closure_kind) = obligation.self_ty().skip_binder().to_opt_closure_kind()
&& let Some(goal_kind) =
obligation.predicate.skip_binder().trait_ref.args.type_at(1).to_opt_closure_kind()
let self_ty = obligation.self_ty().skip_binder();
let target_kind_ty = obligation.predicate.skip_binder().trait_ref.args.type_at(1);

// `to_opt_closure_kind` is kind of ICEy when it sees non-int types.
if !(self_ty.is_integral() || self_ty.is_ty_var()) {
return;
}
if !(target_kind_ty.is_integral() || self_ty.is_ty_var()) {
return;
}

if let Some(closure_kind) = self_ty.to_opt_closure_kind()
&& let Some(goal_kind) = target_kind_ty.to_opt_closure_kind()
{
if closure_kind.extends(goal_kind) {
candidates.vec.push(AsyncFnKindHelperCandidate);
Expand Down
15 changes: 15 additions & 0 deletions tests/ui/async-await/async-closures/arg-mismatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// aux-build:block-on.rs
// edition:2021

#![feature(async_closure)]

extern crate block_on;

fn main() {
block_on::block_on(async {
let c = async |x| {};
c(1i32).await;
c(2usize).await;
//~^ ERROR mismatched types
});
}
21 changes: 21 additions & 0 deletions tests/ui/async-await/async-closures/arg-mismatch.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0308]: mismatched types
--> $DIR/arg-mismatch.rs:12:11
|
LL | c(2usize).await;
| - ^^^^^^ expected `i32`, found `usize`
| |
| arguments to this function are incorrect
|
note: closure parameter defined here
--> $DIR/arg-mismatch.rs:10:24
|
LL | let c = async |x| {};
| ^
help: change the type of the numeric literal from `usize` to `i32`
|
LL | c(2i32).await;
| ~~~

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0308`.
20 changes: 20 additions & 0 deletions tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// aux-build:block-on.rs
// edition:2021
// run-pass

#![feature(async_closure, async_fn_traits)]

extern crate block_on;

use std::ops::AsyncFnOnce;

fn main() {
block_on::block_on(async {
let x = async || {};

async fn needs_async_fn_once(x: impl AsyncFnOnce()) {
x().await;
}
needs_async_fn_once(x).await;
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
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>.
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>.
20 changes: 20 additions & 0 deletions tests/ui/async-await/async-closures/auxiliary/block-on.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// edition: 2021

#![feature(async_closure, noop_waker, async_fn_traits)]

use std::future::Future;
use std::pin::pin;
use std::task::*;

pub fn block_on<T>(fut: impl Future<Output = T>) -> T {
let mut fut = pin!(fut);
// Poll loop, just to test the future...
let ctx = &mut Context::from_waker(Waker::noop());

loop {
match unsafe { fut.as_mut().poll(ctx) } {
Poll::Pending => {}
Poll::Ready(t) => break t,
}
}
}
16 changes: 16 additions & 0 deletions tests/ui/async-await/async-closures/await-inference-guidance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// aux-build:block-on.rs
// edition:2021
// run-pass

#![feature(async_closure)]

extern crate block_on;

fn main() {
block_on::block_on(async {
let x = async |x: &str| -> String { x.to_owned() };
let mut s = x("hello, world").await;
s.truncate(4);
println!("{s}");
});
}
26 changes: 26 additions & 0 deletions tests/ui/async-await/async-closures/brand.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// aux-build:block-on.rs
// edition:2021
// build-pass

#![feature(async_closure, async_fn_traits)]

extern crate block_on;

use std::future::Future;
use std::marker::PhantomData;
use std::ops::AsyncFn;

struct S;
struct B<'b>(PhantomData<&'b mut &'b mut ()>);

impl S {
async fn q<F: AsyncFn(B<'_>)>(self, f: F) {
f(B(PhantomData)).await;
}
}

fn main() {
block_on::block_on(async {
S.q(async |b: B<'_>| { println!("...") }).await;
});
}
18 changes: 18 additions & 0 deletions tests/ui/async-await/async-closures/higher-ranked-return.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// aux-build:block-on.rs
// edition:2021

// known-bug: unknown
// Borrow checking doesn't like that higher-ranked output...

#![feature(async_closure)]

extern crate block_on;

fn main() {
block_on::block_on(async {
let x = async move |x: &str| -> &str {
x
};
let s = x("hello!").await;
});
}
14 changes: 14 additions & 0 deletions tests/ui/async-await/async-closures/higher-ranked-return.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: lifetime may not live long enough
--> $DIR/higher-ranked-return.rs:13:46
|
LL | let x = async move |x: &str| -> &str {
| ________________________________-________----_^
| | | |
| | | return type of async closure `{async closure body@$DIR/higher-ranked-return.rs:13:46: 15:10}` contains a lifetime `'2`
| | let's call the lifetime of this reference `'1`
LL | | x
LL | | };
| |_________^ returning this value requires that `'1` must outlive `'2`

error: aborting due to 1 previous error

14 changes: 10 additions & 4 deletions tests/ui/async-await/async-closures/higher-ranked.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
// aux-build:block-on.rs
// edition:2021
// check-pass
// build-pass

#![feature(async_closure)]

extern crate block_on;

fn main() {
let x = async move |x: &str| {
println!("{x}");
};
block_on::block_on(async {
let x = async move |x: &str| {
println!("{x}");
};
x("hello!").await;
});
}
12 changes: 12 additions & 0 deletions tests/ui/async-await/async-closures/is-not-fn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// edition:2021

#![feature(async_closure)]

fn main() {
fn needs_fn(x: impl FnOnce()) {}
needs_fn(async || {});
//~^ ERROR expected a `FnOnce()` closure, found `{coroutine-closure@
// FIXME(async_closures): This should explain in more detail how async fns don't
// implement the regular `Fn` traits. Or maybe we should just fix it and make them
// when there are no upvars or whatever.
}
19 changes: 19 additions & 0 deletions tests/ui/async-await/async-closures/is-not-fn.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error[E0277]: expected a `FnOnce()` closure, found `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}`
--> $DIR/is-not-fn.rs:7:14
|
LL | needs_fn(async || {});
| -------- ^^^^^^^^^^^ expected an `FnOnce()` closure, found `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}`
| |
| required by a bound introduced by this call
|
= help: the trait `FnOnce<()>` is not implemented for `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}`
= note: wrap the `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}` in a closure with no arguments: `|| { /* code */ }`
note: required by a bound in `needs_fn`
--> $DIR/is-not-fn.rs:6:25
|
LL | fn needs_fn(x: impl FnOnce()) {}
| ^^^^^^^^ required by this bound in `needs_fn`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
20 changes: 20 additions & 0 deletions tests/ui/async-await/async-closures/move-consuming-capture.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// aux-build:block-on.rs
// edition:2021

#![feature(async_closure)]

extern crate block_on;

struct NoCopy;

fn main() {
block_on::block_on(async {
let s = NoCopy;
let x = async move || {
drop(s);
};
x().await;
x().await;
//~^ ERROR use of moved value: `x`
});
}
17 changes: 17 additions & 0 deletions tests/ui/async-await/async-closures/move-consuming-capture.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0382]: use of moved value: `x`
--> $DIR/move-consuming-capture.rs:17:9
|
LL | let x = async move || {
| - move occurs because `x` has type `{coroutine-closure@$DIR/move-consuming-capture.rs:13:17: 13:30}`, which does not implement the `Copy` trait
...
LL | x().await;
| --- `x` moved due to this method call
LL | x().await;
| ^ value used here after move
|
note: `async_call_once` takes ownership of the receiver `self`, which moves `x`
--> $SRC_DIR/core/src/ops/async_function.rs:LL:COL

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0382`.
21 changes: 21 additions & 0 deletions tests/ui/async-await/async-closures/move-is-async-fn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// aux-build:block-on.rs
// edition:2021
// build-pass

#![feature(async_closure)]

extern crate block_on;

fn main() {
block_on::block_on(async {
let s = String::from("hello, world");
let c = async move || {
println!("{s}");
};
c().await;
c().await;

fn is_static<T: 'static>(_: T) {}
is_static(c);
});
}
19 changes: 19 additions & 0 deletions tests/ui/async-await/async-closures/mutate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// aux-build:block-on.rs
// edition:2021
// run-pass

#![feature(async_closure)]

extern crate block_on;

fn main() {
block_on::block_on(async {
let mut prefix = String::from("Hello");
let mut c = async move |x: &str| {
prefix.push(',');
println!("{prefix} {x}!")
};
c("world").await;
c("rust").await;
});
}
21 changes: 21 additions & 0 deletions tests/ui/async-await/async-closures/not-lending.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// aux-build:block-on.rs
// edition:2021

#![feature(async_closure)]

extern crate block_on;

// Make sure that we can't make an async closure that evaluates to a self-borrow.
// i.e. that the generator may reference captures, but the future's return type can't.

fn main() {
block_on::block_on(async {
let s = String::new();
let x = async move || -> &String { &s };
//~^ ERROR lifetime may not live long enough

let s = String::new();
let x = async move || { &s };
//~^ ERROR lifetime may not live long enough
});
}
24 changes: 24 additions & 0 deletions tests/ui/async-await/async-closures/not-lending.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error: lifetime may not live long enough
--> $DIR/not-lending.rs:14:42
|
LL | let x = async move || -> &String { &s };
| ------------------------ ^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of async closure `{async closure body@$DIR/not-lending.rs:14:42: 14:48}` contains a lifetime `'2`
| lifetime `'1` represents this closure's body
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure

error: lifetime may not live long enough
--> $DIR/not-lending.rs:18:31
|
LL | let x = async move || { &s };
| ------------- ^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of async closure `{async closure body@$DIR/not-lending.rs:18:31: 18:37}` contains a lifetime `'2`
| lifetime `'1` represents this closure's body
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure

error: aborting due to 2 previous errors

14 changes: 14 additions & 0 deletions tests/ui/async-await/async-closures/return-type-mismatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// aux-build:block-on.rs
// edition:2021

#![feature(async_closure)]

extern crate block_on;

fn main() {
block_on::block_on(async {
let x = async || -> i32 { 0 };
let y: usize = x().await;
//~^ ERROR mismatched types
});
}
Loading

0 comments on commit ca6c57a

Please sign in to comment.