-
Notifications
You must be signed in to change notification settings - Fork 13k
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
False positive: single_use_lifetimes #77175
Comments
Very strange. I'm thinking we have some stateful HIR lowering code that is hanging onto state it shouldn't. |
This bug existed as far back as November 2019 (when async/.await was stabilized), so it's not a recent regression. |
Marking this as P-medium based on the wg-prioritization discussion |
I did some digging into this. I'm brand new to the compiler so slowly learning the architecture along the way. Below are some thoughts on what I've seen so far. The desugared HIR for both samples is the same: async first
async last
As expected both resolves the same set of lifetimes (from rustc_resolve::late::lifetimes) and process them in the same order (since they are sorted). Dump from the async first
async last
Interestingly both included the |
The examples may be misleading. Here is a new example. #![deny(single_use_lifetimes)]
// error: lifetime parameter `'a` only used once
async fn bar<'a>(s1: String, s2: &'_ str, s3: &'a str) -> &'a str {
s3
} |
Makes sense. Then the behavior is no longer magical and makes a lot of sense. |
Based on what I've seen the desugaring process is working correctly and the I have validated this simple change (I named the loop // if the current type is an opaque type that originates from an async function
if let hir::Node::Item(item) = self.tcx.hir().get(parent_hir_id) {
if let hir::ItemKind::OpaqueTy(ref opaque) = item.kind {
if opaque.origin == hir::OpaqueTyOrigin::AsyncFn {
// check that the lifetimes used in the opaque type are already declared in the current scope.
// The defined_by map is created at the beginning of this method to collect all lifetimes in the scope
for p in opaque.generics.params {
if defined_by.contains_key(&p.name) {
continue 'lifetimes;
}
}
}
}
} |
One more question for the team (@camelid, @Dylan-DPC), what would we expect in this case? #[deny(single_use_lifetimes)]
async fn bar<'a>(s1: String, s2: &'_ str, s3: &'a str) -> String {
s3.to_owned()
} This compiles successfully, which is counter intuitive by looking at the function signature. It compiles because the desugared generator uses the same lifetime as the async function. |
As reported in issue rust-lang#77175, the opaque type generated by the desugaring process of an async function uses the lifetimes defined by the originating function. The definition ID for the lifetimes in the opaque method is different from the one in the originating async function and it could therefore be considered a single use of the lifetimne, this causes the single_use_lifetimes lint to fail compilation if explicitly denied. This fix skips the lint for lifetimes used only once in generated opaque types for an async function that are declared in the parent async function definition.
I tried this code:
I expected to see this happen: the code is correct.
Instead, this happened:
Just swap the functions, then the code compiles:
Update 20201014:
The examples may be misleading.
I forgot that
#[deny(single_use_lifetimes)]
takes effect on a single item.It was a typo.
Here is a new example. It should be correct. But the compiler does not accept it.
Meta
rustc --version --verbose
:rustc +nightly --version --verbose
:The text was updated successfully, but these errors were encountered: