You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Async fn does not compile if lifetime does not appear in bounds (sometimes) #63033
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds --> src/main.rs:7:64 |7 | async fn f<'a>(reference: &(), marker: PhantomData<dyn Trait>) {} | ^ |note: hidden type `impl std::future::Future` captures the scope of call-site for function at 7:64 --> src/main.rs:7:64 |7 | async fn f<'a>(reference: &(), marker: PhantomData<dyn Trait>) {} | ^^
I believe this should compile because:
The non-async function with the same signature does compile;
Reacted by Ophir LOJKINE, R+, Nazar Mokrynskyi, Francesco Cinà, Tux3, Ilia Konnov, Jan, Everton Patitucci, Heinz N. Gies, Dario Nieuwenhuis and 19 more
Reacted by Heinz N. Gies, Marvin Löbel, Brandon H. Gomes, João Marcos and Vinícius Miguel
Check-in from WG-async-await meeting: I agree this is a bug -- it doesn't necessarily block stabilization (no fwd compat risk) but would definitely be a good one to fix. Marking as blocking + unclear for the moment and will return to it.
I did a bit of investigation. The error results because we have an inference variable ('_#3r) that seems to have no constraints, but must be equal to one of the captured regions (e.g., 'a). I haven't 100% figured out where this variable comes from, I imagine it's related to 'a though? In any case, the resolver basically winds up in a situation where it could pick any of the lifetimes but has no reason to prefer one over the other, and hence it reports an error.
OK, I think I see what's going on. It doesn't actually have much to do with the unused lifetime per se. What happens is this:
The type of the generated result is something like Foo<&'2 (), PhantomData<dyn Trait + '3>> -- i.e., it includes the types of the parameters, though with region variables in place of the known regions. Those types must be supertypes of the actual parameter types.
We capture the argument marker of type dyn Trait + 'static -- per the subtyping rules, we create the type dyn Trait + '3 for the type of this captured value, with the requirement that 'static: '3 -- as it happens, this is always true, so there is effectively no constraint on '3.
We do impose the constraint that '2 and '3 must both be equal to one of the free regions -- in this case, 'a or the anonymous lifetime of reference (which I'll call 'b).
So thus the region solvers get in a state where '3 must be equal to 'a, 'b, or 'static -- but any one of them would be a reasonable choice.
Removing the unused lifetime ('a) makes this an easy choice -- it can pick 'b, which is smaller than 'static. (Per the "least choice" rules we usually use.)
Removing the type and putting it into a struct (Wrapper) removes the lifetime variable altogether, as then we have Foo<&'2 (), Wrapper>, and there are no lifetime parameters involved.
I'm not sure what I think is the best fix. It would be nice for the solver to pick 'static, though it complicates the algorithm. You could certainly imagine a tweak that picks 'static if the value is legal and otherwise looks for a least choice. That might indeed be superior to the current approach, though I'd want to think about the full implications -- in particular, when impl Trait is used in places outside of the fn return type (e.g., in a let statement), picking 'static might lead to unnecessary borrow check errors. (The alternative would be to only look for 'static when there is no least choice; that seems safer.)
(Interestingly, at some point at least I thought that a polonius-like approach sidesteps a lot of the challenges here-- if we had confidence that we would transition that might ease some of my concerns.)
Now that I understand better what is happening, I don't feel this has to block stabilization -- in particular, I don't think that there is a forward compatibility risk from us improving the region inference algorithm. I don't think we would choose an algorithm that makes existing code illegal. =)
As such, I'm going to mark this as Deferred.
Reacted by Mazdak Farrokhzad, Taylor Cramer and Hannah Stevenson
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
--> src/lib.rs:4:54
|
4 | async fn wat(&self, _: &'static str, _: Bar<'_>) {}
| ^
|
note: hidden type `impl std::future::Future` captures the scope of call-site for function at 4:54
--> src/lib.rs:4:54
|
4 | async fn wat(&self, _: &'static str, _: Bar<'_>) {}
| ^^
error: aborting due to previous error
Removing any of the three arguments to the function allows it to compile.
Reacted by Jacob Adam, Kahlil Abreu, ChrisGreenaway, Andreas Sommer and Bernhard Schuster
I am also affected by this bug, where having both a reference and a box as parameters to a function causes the cryptic "hidden type for impl Trait captures lifetime that does not appear in bounds" :
Activity
nikomatsakis commentedon Aug 13, 2019
Check-in from WG-async-await meeting: I agree this is a bug -- it doesn't necessarily block stabilization (no fwd compat risk) but would definitely be a good one to fix. Marking as blocking + unclear for the moment and will return to it.
nikomatsakis commentedon Aug 14, 2019
I did a bit of investigation. The error results because we have an inference variable (
'_#3r) that seems to have no constraints, but must be equal to one of the captured regions (e.g.,'a). I haven't 100% figured out where this variable comes from, I imagine it's related to'athough? In any case, the resolver basically winds up in a situation where it could pick any of the lifetimes but has no reason to prefer one over the other, and hence it reports an error.nikomatsakis commentedon Aug 14, 2019
OK, I think I see what's going on. It doesn't actually have much to do with the unused lifetime per se. What happens is this:
Foo<&'2 (), PhantomData<dyn Trait + '3>>-- i.e., it includes the types of the parameters, though with region variables in place of the known regions. Those types must be supertypes of the actual parameter types.markerof typedyn Trait + 'static-- per the subtyping rules, we create the typedyn Trait + '3for the type of this captured value, with the requirement that'static: '3-- as it happens, this is always true, so there is effectively no constraint on'3.'2and'3must both be equal to one of the free regions -- in this case,'aor the anonymous lifetime ofreference(which I'll call'b).So thus the region solvers get in a state where
'3must be equal to'a,'b, or'static-- but any one of them would be a reasonable choice.Removing the unused lifetime (
'a) makes this an easy choice -- it can pick'b, which is smaller than'static. (Per the "least choice" rules we usually use.)Removing the type and putting it into a struct (
Wrapper) removes the lifetime variable altogether, as then we haveFoo<&'2 (), Wrapper>, and there are no lifetime parameters involved.I'm not sure what I think is the best fix. It would be nice for the solver to pick
'static, though it complicates the algorithm. You could certainly imagine a tweak that picks'staticif the value is legal and otherwise looks for a least choice. That might indeed be superior to the current approach, though I'd want to think about the full implications -- in particular, whenimpl Traitis used in places outside of the fn return type (e.g., in aletstatement), picking'staticmight lead to unnecessary borrow check errors. (The alternative would be to only look for'staticwhen there is no least choice; that seems safer.)(Interestingly, at some point at least I thought that a polonius-like approach sidesteps a lot of the challenges here-- if we had confidence that we would transition that might ease some of my concerns.)
nikomatsakis commentedon Aug 14, 2019
Now that I understand better what is happening, I don't feel this has to block stabilization -- in particular, I don't think that there is a forward compatibility risk from us improving the region inference algorithm. I don't think we would choose an algorithm that makes existing code illegal. =)
As such, I'm going to mark this as Deferred.
sfackler commentedon Oct 10, 2019
Here's another example of the same problem that doesn't have an unused lifetime (AFAIKT):
Removing any of the three arguments to the function allows it to compile.
lovasoa commentedon Dec 14, 2019
I am also affected by this bug, where having both a reference and a box as parameters to a function causes the cryptic "hidden type for
impl Traitcaptures lifetime that does not appear in bounds" :(playground)
Is there a resolution in sight ?
58 remaining items
Rollup merge of rust-lang#105300 - aliemjay:member-lower, r=oli-obk
Rollup merge of rust-lang#105300 - aliemjay:member-lower, r=oli-obk
runtime-rs: update rust to 1.69.0
runtime-rs: update rust to 1.69.0
runtime-rs: update rust to 1.69.0
runtime-rs: update rust to 1.69.0
runtime-rs: update rust to 1.69.0
runtime-rs: update rust to 1.69.0
runtime-rs: update rust to 1.69.0
remove lifetime workaround now that this is fixed (#1413)
runtime-rs: update rust to 1.69.0
refactor: remove internal Captures workaround trait