-
-
Notifications
You must be signed in to change notification settings - Fork 85
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
Transform blocks into async move
for more compatibility
#143
Conversation
cc @nightmared @hawkw re: #45/#97 |
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.
Thanks, this is great. I am on board with this if we can get the regression rate low. Luckily I have a work codebase with thousands of uses of #[async_trait]. A significant fraction of them are broken by this PR, so I will get started on triaging those.
Awesome! Let me know if I can help with triaging some of those! |
src/expand.rs
Outdated
// let x = x; | ||
// let (a, b) = __arg1; | ||
// | ||
// __self + x |
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.
// __self + x | |
// __self + x + a + b |
A lot of things that use use async_trait::async_trait;
#[async_trait]
trait Trait {
async fn f(self);
}
#[async_trait]
impl Trait for String {
async fn f(self) { // same for &mut self
println!("{}", self);
}
} error[E0382]: borrow of moved value: `self`
--> src/main.rs:11:27
|
10 | async fn f(self) {
| ---- value moved here
11 | println!("{}", self);
| ^^^^ value borrowed here after move
|
= note: move occurs because `self` has type `String`, which does not implement the `Copy` trait |
Ah, that one's easy. So the We could visit the tokens in the token stream and rename those Alas, I'd hope there was something more clever we could do with |
Now that I take a closer look at your original implementation, I see that this is exactly what you used to do. |
Here is a "type mismatch" failure that appears to be quite common: use async_trait::async_trait;
#[async_trait]
pub trait Trait {
async fn f() -> Box<dyn Iterator<Item = ()>> {
Box::new(std::iter::empty())
}
} error[E0271]: type mismatch resolving `<impl Future as Future>::Output == Box<(dyn Iterator<Item = ()> + 'static)>`
--> src/main.rs:5:50
|
5 | async fn f() -> Box<dyn Iterator<Item = ()>> {
| __________________________________________________^
6 | | Box::new(std::iter::empty())
7 | | }
| |_____^ expected trait object `dyn Iterator`, found struct `std::iter::Empty`
|
= note: expected struct `Box<(dyn Iterator<Item = ()> + 'static)>`
found struct `Box<std::iter::Empty<_>>`
= note: required for the cast to the object type `dyn Future<Output = Box<(dyn Iterator<Item = ()> + 'static)>> + Send` |
I think those are the big two. Will check again after those are fixed. There is a third category which is some logging-related proc macros doing something tracing-like on the inner code but we can get that fixed to recognize |
One more thing: this regresses #105 on compilers 1.46 and older, which is this failure in the 1.40 CI job: error[E0425]: cannot find value `__self` in this scope
--> tests/test.rs:599:13
|
599 | #[async_trait]
| ^^^^^^^^^^^^^^ not found in this scope If it's easy, it would be good to get that worked around again even if new rustc fixed the bug. It was only a 1 line workaround in #105. |
I'll take a look at this. I guess the proper course of action would be to support both the old and the new behavior ? |
I tried the latest commit -- it's regressing some code like this: use async_trait::async_trait;
#[async_trait]
pub trait Trait: Sized {
async fn f(self) {
struct Struct;
impl Struct {
fn f(self) {
let _ = self;
}
}
}
} error[E0434]: can't capture dynamic environment in a fn item
--> src/main.rs:9:25
|
9 | let _ = self;
| ^^^^
|
= help: use the `|| { ... }` closure form instead |
Also this: #![deny(warnings)]
use async_trait::async_trait;
#[async_trait]
pub trait Trait {
async fn f() {
unimplemented!()
}
} error: unreachable expression
--> src/main.rs:7:18
|
7 | async fn f() {
| __________________^
8 | | unimplemented!()
| | ---------------- any code following this expression is unreachable
9 | | }
| |_____^ unreachable expression |
Alright, that last commit should handle all of the regressions thus far. As a side effect, some error messages have improved. Feel free to hit me with more regressions. I've also marked the |
Oh, perhaps I missed the point of the unreachable example you showed. I see the deny warnings now. Makes sense. Will take a look a bit later. Edit: handled. |
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.
This looks good to me.
I'll give a little time before merging to give the tracing
folks a chance to get an update out.
tests/ui/self-span.stderr
Outdated
error[E0308]: mismatched types | ||
--> $DIR/self-span.rs:25:21 | ||
--> $DIR/self-span.rs:24:21 | ||
| | ||
24 | async fn method(self) { | ||
| ^^^^ expected `()`, found enum `E` | ||
25 | let _: () = self; | ||
| -- ^^^^ expected `()`, found enum `E` | ||
| | | ||
| expected due to this | ||
| -- expected due to this |
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.
Do you have any insight into what's going on with this error message? The error before this PR is what I would expect and matches what you get in the non-trait case: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f8833ba4ae1fad764bbaa11f1f6f2701. The error after seems misplaced to me.
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.
Ah, interesting. I had read this message differently and believed it to be an improved version of the previous one, but I see now what the intention of the message is and how this is now a bit more confusing.
The span for that second self
, which is renamed to __self
, is set to the span of the receiver to fix #105. Besides being able to change only the resolution context, I'm not sure what heuristic to apply here to change only affected self
s.
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.
I'm really not sure what to do here. The previous workaround seemed to "work" because we generated a new function. Now that we don't do so, we need to replace the span of all self
s, not just of the one argument to the function as before. This will result in all error messages that correspond to self
to point to the receiver, which is an unfortunate compromise.
My suggestion is that we gate this workaround to rustc versions <= 1.45, or whenever the fix landed in the compiler.
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.
Sounds good, let's do that. Thanks!
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.
Have a preference for library to use? My go-to is versioncheck.
Awesome. I'll remove all of the unused code then. |
On some minor note, it is not so easy to handle arguments that aren't used inside the function: tokio-rs/tracing#1228 (comment). Box::pin(async move {
let __ret: () = {
let mut __self = self;
let v = v;
};
#[allow(unreachable_code)]
__ret
}) So far, it have the same behavior as an equivalent sync function would have, so the warning is expected, and thus not an issue. Box::pin(async move {
XXX // something that uses the `v` variable
tracing::Instrument::instrument(
async move {
{
let __ret: () = {
let mut __self = self;
let v = v;
};
#[allow(unreachable_code)]
__ret
}
},
__tracing_attr_span,
)
.await
}) In that case, the
I'm not really sure about the way to go here. Optimally, we would inject |
@nightmared, would it help if we insert some unique distinguishing marker like Box::pin(async move {
let __ret: #RETURN = {
let v = v;
let __async_trait: ();
#BODY
};
#[allow(unreachable_code)]
__ret
}) You could crawl the macro input and replace that marker with: Box::pin(async move {
let __ret: #RETURN = {
let v = v;
if false {
let _ = &(v + 5); // expr from #[instrument(fields(test=%v+5))]
}
#BODY
};
#[allow(unreachable_code)]
__ret
}) |
Hmm yes, i guess this would work! However that also means we would have to follow variable reassignments like |
I have started to take a look at @dtolnay's solution, but it isn't quite as trivial as I first thought because we need to keep track of variable reassignments (e.g. |
To propose a different solution: what if nothing is done about the warning? The warning feels correct to me: the variable is declared and never used in the function body by the written code. Purposefully silencing this warning could result in incorrect code that otherwise would have been caught. It's also easy enough to add an |
That is a possibility yes. However with some very minimal changes in async-trait (nightmared@32b1573) and a bit more work on tracing side, I was able to get this minor nitpick working ;) |
b9c3a59
to
0cda89b
Compare
@dtolnay I'm continuing to work to improve |
I am getting rather odd build errors with a minimal project with commit 0cda89b somehow: #[async_trait::async_trait]
pub trait Derp {
async fn hi();
} fails to build with internal errors, both latest stable and nightly. Note that I literally just have those 4 lines + a dummy For some reason, using that exact same commit in a way bigger project of mine compiles, I am properly confused about that elusive build error. |
@Kiiyya Your larger project is probably enabling features of |
Yep works now, that was quick, thank you! |
Box::pin(async move { | ||
let __ret: #ret_ty = { | ||
#(#decls)* | ||
#(#stmts)* |
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.
In order for tokio-rs/tracing#1228 to work well, could you consider adding let __async_trait: ();
between the #(#decls)*
and #(#stmts)*
lines? That should do the job. Thanks!
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.
I think this is a poor solution to the problem at hand, and that emitting warnings is, in fact, the desired effect. Nevertheless, I've pushed a commit that implements this.
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.
I do agree that it's a hack, and a nasty one at that. On the other hand, I do not believe that users will understand that exhibit A will trigger a warning and not exhibit B. That doesn't appear very coherent (at least with what I think is the behavior of instrument
in non-async-trait functions) either.
Exhibit A:
#[instrument(fields(test=%v+5))]
async fn call(&mut self, v: usize) {}
Exhibit B:
#[instrument(fields(test=%_v+5))]
async fn call(&mut self, _v: usize) {}
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.
I disagree that users won't understand. Without the attribute, A
triggers a warning and B
does not because v
is unused in the body of A
. Even when the attribute is applied, v
continues to be unused in A
, and so it is my opinion that A
should continue to result in a warning.
I don't mean to imply that instrument
should inconsistently emit warnings based on whether async-trait
is used, but instead, that it should consistently emit warnings as would be emitted without the attribute.
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.
Thanks, this is great.
I ended up landing this without the workaround for tracing-attributes because #143 (comment) / #143 (comment) are compelling to me. |
This is effectively #125 with correct drop ordering. I've kept the changes minimal, hence the temporary
allow(dead_code, unused_imports)
, which will be removed once this is approved and I sweep out the now-unused code.This breaks #45/#97, which from a cursory reading, looks like is a result of hard-coding previous functionality into
tracing
, though do let me know if there's something I can do here to maintain that compatibility.I should add: this makes quite a few more
impl
s in Rocket compile without needing to manually expand the async-trait or other hackery. This fixes all of the cases related to #61 in Rocket. My guess, though I haven't tested, is that this fixes #126 as well.