-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add shorter and more direct error for dyn AsyncFn #133267
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
The `Async{Fn, FnMut, FnOnce}` traits are not yet dyn-compatible. | ||
|
||
Erroneous code example: | ||
|
||
```compile_fail,E0802,edition2018 | ||
#![feature(async_closure)] | ||
use core::ops::AsyncFn; | ||
|
||
async fn call_async_fn_twice(some_fn: &dyn AsyncFn()) { | ||
some_fn().await; | ||
some_fn().await; | ||
} | ||
``` | ||
|
||
One workaround to this issue is to use `impl Async...` instead: | ||
|
||
```edition2018 | ||
#![feature(async_closure)] | ||
use core::ops::AsyncFn; | ||
|
||
async fn call_async_fn_twice(some_fn: &impl AsyncFn()) { | ||
some_fn().await; | ||
some_fn().await; | ||
} | ||
``` | ||
|
||
This error indicates that you attempted to use `dyn AsyncFn`, | ||
`dyn AsyncFnMut`, or `dyn AsyncFnOnce`. | ||
|
||
This is not yet possible because the `Async...` traits internally return | ||
a concrete `Future` associated type. For dynamic callsites, it is impossible | ||
to know the size of the returned `Future` object since different | ||
`Async...` implementations may return differently-sized `Future` objects. | ||
|
||
This is analogous to the more general issue of creating a `dyn` type without | ||
specifying associated types, e.g. `dyn Iterator` as opposed to | ||
`dyn Iterator<Item = SomeItemType>`. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -541,6 +541,7 @@ E0798: 0798, | |
E0799: 0799, | ||
E0800: 0800, | ||
E0801: 0801, | ||
E0802: 0802, | ||
); | ||
) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,16 @@ pub fn hir_ty_lowering_dyn_compatibility_violations( | |
trait_def_id: DefId, | ||
) -> Vec<DynCompatibilityViolation> { | ||
debug_assert!(tcx.generics_of(trait_def_id).has_self); | ||
|
||
// Check for `AsyncFn` traits first to avoid reporting various other | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't necessary, is it? I would suppose removing this doesn't actually affect anything. |
||
// errors about the details of why these traits aren't dyn-compatible. | ||
for supertrait in tcx.supertrait_def_ids(trait_def_id) { | ||
if tcx.async_fn_trait_kind_from_def_id(supertrait).is_some() { | ||
let fn_trait = tcx.item_name(supertrait); | ||
return vec![DynCompatibilityViolation::AsyncFnTrait { fn_trait }]; | ||
} | ||
} | ||
|
||
tcx.supertrait_def_ids(trait_def_id) | ||
.map(|def_id| predicates_reference_self(tcx, def_id, true)) | ||
.filter(|spans| !spans.is_empty()) | ||
|
@@ -53,6 +63,17 @@ fn dyn_compatibility_violations( | |
debug_assert!(tcx.generics_of(trait_def_id).has_self); | ||
debug!("dyn_compatibility_violations: {:?}", trait_def_id); | ||
|
||
// Check for `AsyncFn` traits first to avoid reporting various other | ||
// errors about the details of why these traits aren't dyn-compatible. | ||
for supertrait in tcx.supertrait_def_ids(trait_def_id) { | ||
if tcx.async_fn_trait_kind_from_def_id(supertrait).is_some() { | ||
let fn_trait = tcx.item_name(supertrait); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. defer this call to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, lol, you can't. If you want, you could probably remove the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I did try that initially! Hence the comments about my annoyance that I couldn't put a |
||
return core::slice::from_ref( | ||
tcx.arena.alloc(DynCompatibilityViolation::AsyncFnTrait { fn_trait }), | ||
); | ||
} | ||
} | ||
|
||
tcx.arena.alloc_from_iter( | ||
tcx.supertrait_def_ids(trait_def_id) | ||
.flat_map(|def_id| dyn_compatibility_violations_for_trait(tcx, def_id)), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Test the diagnostic output (error descriptions) resulting from `dyn AsyncFn{,Mut,Once}`. | ||
//@ edition:2018 | ||
cramertj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#![feature(async_closure)] | ||
|
||
use core::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; | ||
|
||
// --- Explicit `dyn` --- | ||
|
||
fn takes_async_fn(_: &dyn AsyncFn()) {} | ||
//~^ ERROR the trait `AsyncFn` is not yet dyn-compatible | ||
|
||
fn takes_async_fn_mut(_: &mut dyn AsyncFnMut()) {} | ||
//~^ ERROR the trait `AsyncFnMut` is not yet dyn-compatible | ||
|
||
fn takes_async_fn_once(_: Box<dyn AsyncFnOnce()>) {} | ||
//~^ ERROR the trait `AsyncFnOnce` is not yet dyn-compatible | ||
|
||
// --- Non-explicit `dyn` --- | ||
|
||
#[allow(bare_trait_objects)] | ||
fn takes_async_fn_implicit_dyn(_: &AsyncFn()) {} | ||
//~^ ERROR the trait `AsyncFn` is not yet dyn-compatible | ||
|
||
#[allow(bare_trait_objects)] | ||
fn takes_async_fn_mut_implicit_dyn(_: &mut AsyncFnMut()) {} | ||
//~^ ERROR the trait `AsyncFnMut` is not yet dyn-compatible | ||
|
||
#[allow(bare_trait_objects)] | ||
fn takes_async_fn_once_implicit_dyn(_: Box<AsyncFnOnce()>) {} | ||
//~^ ERROR the trait `AsyncFnOnce` is not yet dyn-compatible | ||
|
||
// --- Supertrait --- | ||
|
||
trait SubAsyncFn: AsyncFn() {} | ||
fn takes_sub_async_fn(_: &dyn SubAsyncFn) {} | ||
//~^ ERROR the trait `SubAsyncFn` cannot be made into an object | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
error[E0802]: the trait `AsyncFn` is not yet dyn-compatible | ||
--> $DIR/dyn.rs:10:23 | ||
| | ||
LL | fn takes_async_fn(_: &dyn AsyncFn()) {} | ||
| ^^^^^^^^^^^^^ `AsyncFn` is not yet dyn compatible | ||
| | ||
help: consider using an opaque type instead | ||
| | ||
LL | fn takes_async_fn(_: &impl AsyncFn()) {} | ||
| ~~~~ | ||
|
||
error[E0802]: the trait `AsyncFnMut` is not yet dyn-compatible | ||
--> $DIR/dyn.rs:13:31 | ||
| | ||
LL | fn takes_async_fn_mut(_: &mut dyn AsyncFnMut()) {} | ||
| ^^^^^^^^^^^^^^^^ `AsyncFnMut` is not yet dyn compatible | ||
| | ||
help: consider using an opaque type instead | ||
| | ||
LL | fn takes_async_fn_mut(_: &mut impl AsyncFnMut()) {} | ||
| ~~~~ | ||
|
||
error[E0802]: the trait `AsyncFnOnce` is not yet dyn-compatible | ||
--> $DIR/dyn.rs:16:31 | ||
| | ||
LL | fn takes_async_fn_once(_: Box<dyn AsyncFnOnce()>) {} | ||
| ^^^^^^^^^^^^^^^^^ `AsyncFnOnce` is not yet dyn compatible | ||
| | ||
help: consider using an opaque type instead | ||
| | ||
LL | fn takes_async_fn_once(_: Box<impl AsyncFnOnce()>) {} | ||
| ~~~~ | ||
|
||
error[E0802]: the trait `AsyncFn` is not yet dyn-compatible | ||
--> $DIR/dyn.rs:22:36 | ||
| | ||
LL | fn takes_async_fn_implicit_dyn(_: &AsyncFn()) {} | ||
| ^^^^^^^^^ `AsyncFn` is not yet dyn compatible | ||
| | ||
help: consider using an opaque type instead | ||
| | ||
LL | fn takes_async_fn_implicit_dyn(_: &impl AsyncFn()) {} | ||
| ++++ | ||
|
||
error[E0802]: the trait `AsyncFnMut` is not yet dyn-compatible | ||
--> $DIR/dyn.rs:26:44 | ||
| | ||
LL | fn takes_async_fn_mut_implicit_dyn(_: &mut AsyncFnMut()) {} | ||
| ^^^^^^^^^^^^ `AsyncFnMut` is not yet dyn compatible | ||
| | ||
help: consider using an opaque type instead | ||
| | ||
LL | fn takes_async_fn_mut_implicit_dyn(_: &mut impl AsyncFnMut()) {} | ||
| ++++ | ||
|
||
error[E0802]: the trait `AsyncFnOnce` is not yet dyn-compatible | ||
--> $DIR/dyn.rs:30:44 | ||
| | ||
LL | fn takes_async_fn_once_implicit_dyn(_: Box<AsyncFnOnce()>) {} | ||
| ^^^^^^^^^^^^^ `AsyncFnOnce` is not yet dyn compatible | ||
| | ||
help: consider using an opaque type instead | ||
| | ||
LL | fn takes_async_fn_once_implicit_dyn(_: Box<impl AsyncFnOnce()>) {} | ||
| ++++ | ||
|
||
error[E0038]: the trait `SubAsyncFn` cannot be made into an object | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We already have an error code for object safety, why add another one? Also, I am a bit unsatisfied with the inconsistency in the error messaging between the error message above and this one. If we want to have a separate error message, it should probably read somewhat parallel to the E0038 error message. I don't believe users really need an in-depth explanation of the implementation details for why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't use the language "cannot be made into an object" in the new error message because my understanding was that "object-safety" is the deprecated term, and "dyn-compatibility" is the new one. I also didn't change both to use "dyn-compatibility" because that would have affected significantly more code, though I can send a separate PR for that if it is desired. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think just making this say the same thing as what E0038 says by default is fine, and if you'd like to reword the general error message in a separate PR it should be done separately. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO the net effect of this PR should be to deduplicate the many different dyn compatibility violations, and to add a note explaining that AsyncFn* is not object safe. Adding a totally different error message and a new error code seem unnecessary on top of that I feel? |
||
--> $DIR/dyn.rs:36:27 | ||
| | ||
LL | fn takes_sub_async_fn(_: &dyn SubAsyncFn) {} | ||
| ^^^^^^^^^^^^^^ `SubAsyncFn` cannot be made into an object | ||
| | ||
= note: the trait cannot be made into an object because `async` function traits are not yet dyn-compatible | ||
= note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> | ||
help: consider using an opaque type instead | ||
| | ||
LL | fn takes_sub_async_fn(_: &impl SubAsyncFn) {} | ||
| ~~~~ | ||
|
||
error: aborting due to 7 previous errors | ||
|
||
Some errors have detailed explanations: E0038, E0802. | ||
For more information about an error, try `rustc --explain E0038`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't actually use this anywhere?