-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Suggestion for 'static impl Trait return #51444
Conversation
When encountering a named or anonymous sup requirement (for example, `&'a self`) and a `'static` impl Trait return type, suggest adding the `'_` lifetime constraing to the return type.
r? @cramertj (rust_highfive has picked a reviewer for you, use r? to override) |
LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> { | ||
| ----------------------- this return type evaluates to the `'static` lifetime... | ||
LL | self.x.iter().map(|a| a.0) | ||
| ------ ^^^^ can't infer an appropriate lifetime |
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.
Nit: this cant infer an appropriate lifetime
hint seems out-of-place to me. it interjects in the middle of the sentence "this return type evaluates to the 'static
lifetime but this borrow...". Perhaps move it or combine it somehow with the "can't outlive the anonymous lifetime" error?
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.
Agree. I tried a couple of different outputs, including:
error: can't infer an appropriate lifetime
--> file.rs:11:16
|
11 | self.x.iter().map(|a| a.0)
| ^^^^ can't infer an appropriate lifetime for this
|
note: this return type evaluates to the `'static` lifetime...
--> file.rs:10:35
|
10 | fn iter_values_anon(&self) -> impl Iterator<Item=u32> {
| ^^^^^^^^^^^^^^^^^^^^^^^
note: ...but this borrow...
--> file.rs:11:9
|
11 | self.x.iter().map(|a| a.0)
| ^^^^^^
note: ...can't outlive the anonymous lifetime #1 defined on the method body at 10:5
--> file.rs:10:5
|
10 | / fn iter_values_anon(&self) -> impl Iterator<Item=u32> {
11 | | self.x.iter().map(|a| a.0)
12 | | }
| |_____^
help: you can add a constraint to make the `impl Trait` non`'static` and match the anonymous lifetime #1 defined on the method body at 10:5
|
10 | fn iter_values_anon(&self) -> impl Iterator<Item=u32> + '_ {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I feel that the current output without any primary label would look reasonably good.
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 actually like what you have above, but I think you could remove the "can't infer an appropriate lifetime for this" at the top, since that's covered in the "but this borrow... can't outlive the anonymous lifetime".
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.
Can you take a look at the current output? I removed the primary label and it does seem slightly easier to read.
less than `'static` and match {}", | ||
lifetime, | ||
), | ||
format!("{} + '_", snippet), |
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 the case where the lifetime is named, think it would be more useful to recommend adding a named lifetime here (rather than '_
).
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.
Agree somewhat, the only reason I didn't implement it that way is that from my cursory tests '_
always works, but I could contrive a case where 'a
wouldn't. That being said, I can change it if you feel strongly enough (or we could also leave that as a follow up task).
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.
That makes sense. Thanks for the investigation!
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.
Changed to suggest 'a
.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
cc @nikomatsakis you might be interested in providing feedback here too. |
r=me once @nikomatsakis has had a chance to look, too. I still feel strange about recommending |
@cramertj I can add the code to do that in the trivial case ( |
@estebank That sounds worth it to me! By " |
@cramertj right |
Ping. |
) => { | ||
let anon_reg_sup = self.is_suitable_region(sup_r)?; | ||
if sub_r == &RegionKind::ReStatic && | ||
self._is_return_type_impl_trait(anon_reg_sup.def_id) |
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.
Nit: We don't normally use method names with a leading _
.
sup_r, | ||
) => { | ||
let anon_reg_sup = self.is_suitable_region(sup_r)?; | ||
if sub_r == &RegionKind::ReStatic && |
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.
Are we just concluding that any time that we have a bound of 'static
and a fn with an impl Trait return, the two are connected?
e.g., what happens with this example?
use std::fmt::Debug;
fn foo(x: &u32) -> impl Debug {
let _: &'static u32 = 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.
Are we just concluding that any time that we have a bound of 'static and a fn with an impl Trait return, the two are connected?
Close, unless I misunderstand, the code checks that the return type is impl Trait
and that it is bound to 'static
.
what happens with
The two provided examples keep the current output:
error[E0621]: explicit lifetime required in the type of `x`
--> file.rs:3:25
|
2 | fn foo(x: &u32) -> impl Debug {
| - consider changing the type of `x` to `&'static u32`
3 | let _: &'static u32 = x;
| ^ lifetime `'static` required
error[E0621]: explicit lifetime required in the type of `x`
--> file.rs:7:29
|
7 | fn bar(x: &u32, y: &u32) -> impl Debug {
| - ^^^^^^^^^^ lifetime `'static` required
| |
| consider changing the type of `x` to `&'static u32`
For the second case, we could expand this code handle it and suggest the following, but it'll be very involved:
fn bar<'a>(x: &'a u32, y: &u32) -> impl Debug + 'a {
x
}
I agree with @cramertj that always suggesting Another related example: use std::fmt::Debug;
fn foo(x: &u32, y: &u32) -> impl Debug {
x
} if we suggest |
Output for the two samples doesn't change:
Now using the lifetime name if available, as reflected in the test:
|
Ping from triage @nikomatsakis! This PR needs your review. |
Ok, try this example @estebank use std::fmt::Debug;
struct Foo { }
impl Foo {
fn foo(&self) -> impl Debug {
let _: &'static Foo = self;
()
}
}
fn main() { } I think the reason that this code didn't fire is because a "better" pretty-printed region error was triggering. |
) => { | ||
let anon_reg_sup = self.is_suitable_region(sup_r)?; | ||
if sub_r == &RegionKind::ReStatic && | ||
self.is_return_type_impl_trait(anon_reg_sup.def_id) |
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 that it would be better if we could somehow detect that the 'static
was actually coming from the impl trait (as opposed to some other source). One way to do that might be to make use of the sub_origin
variable -- but we'd have to update the origin that appears here:
rust/src/librustc/infer/anon_types/mod.rs
Line 367 in b7c6e8f
let bound_region = |r| self.sub_regions(infer::CallReturn(span), least_region, r); |
and maybe here too, though that could come later and is a bit more annoying (since that is an obligation cause). The current values are totally bogus anyway: they claim that impl Trait
requirements arise "because returns types must be sized", which is just..not even kind of true.
Anyway, if we added a new SubregionOrigin
for "impl trait obligation" and then checked here for that case, I think this would continue to work in the cases that we want, but rule out the cases we don't.
That said, I am not that keen on adding more uses of SubregionOrigin
, which is "deprecated" (essentially) in the NLL checker. Still, I'm not sure what better thing to use for now.
The output for
is
|
@estebank huh, ok. Why doesn't your new error message trigger in that case? |
Still checking, but it seems that case is not a It doesn't trigger because it only triggers on I feel pretty comfortable merging this because it is purposely restricted to a specific case which is also a common pitfall for people trying |
OK, I still feel like the test is too broad, but I guess we can wait for someone to report problems to us. |
@bors r+ |
📌 Commit 612657d has been approved by |
Suggestion for 'static impl Trait return When encountering a named or anonymous sup requirement (for example, `&'a self`) and a `'static` impl Trait return type, suggest adding the `'_` lifetime constraing to the return type. Fix #43719, #51282. ``` error: cannot infer an appropriate lifetime --> $DIR/static-return-lifetime-infered.rs:17:16 | LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> { | ----------------------- this return type evaluates to the `'static` lifetime... LL | self.x.iter().map(|a| a.0) | ------ ^^^^ | | | ...but this borrow... | note: ...can't outlive the anonymous lifetime #1 defined on the method body at 16:5 --> $DIR/static-return-lifetime-infered.rs:16:5 | LL | / fn iter_values_anon(&self) -> impl Iterator<Item=u32> { LL | | self.x.iter().map(|a| a.0) LL | | } | |_____^ help: you can add a constraint to the return type to make it last less than `'static` and match the anonymous lifetime #1 defined on the method body at 16:5 | LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> + '_ { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ```
☀️ Test successful - status-appveyor, status-travis |
When encountering a named or anonymous sup requirement (for example,
&'a self
) and a'static
impl Trait return type, suggest adding the'_
lifetime constraing to the return type.Fix #43719, #51282.