-
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
Temporary lifetimes sometimes yield surprising errors #46413
Comments
I'm not sure if this is related. Someone on IRC got a borrow error I couldn't explain and this is a minimized reproduction of the error: playground pub struct Statement;
pub struct Rows<'stmt>(&'stmt Statement);
impl<'stmt> Drop for Rows<'stmt> {
fn drop(&mut self) {}
}
impl<'stmt> Iterator for Rows<'stmt> {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
None
}
}
fn get_names() -> Option<String> {
let stmt = Statement;
let mut rows = Rows(&stmt);
rows.map(|row| row).next()
// Replacing the previous line with the following works:
// let x = rows.map(|row| row).next();
// x
//
// Removing the map works too as does removing the Drop impl.
}
fn main() {} Dubious error:
|
This should be fixed with non-lexical lifetimes (playground link). I don't think it's worth fixing the issue separately, since (if I understood correctly) NLL are planned to be stabilized this year. Closing this. |
@pietroalbini : Thank you for looking into it. The second example still does not compile with NLL. Maybe they are unrelated and I should open another issue? See playground: #![feature(nll)]
pub struct Statement;
pub struct Rows<'stmt>(&'stmt Statement);
impl<'stmt> Drop for Rows<'stmt> {
fn drop(&mut self) {}
}
impl<'stmt> Iterator for Rows<'stmt> {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
None
}
}
fn get_names() -> Option<String> {
let stmt = Statement;
let rows = Rows(&stmt);
rows.map(|row| row).next()
// Replacing the previous line with the following works:
// let x = rows.map(|row| row).next();
// x
//
// Removing the map works too as does removing the Drop impl.
}
fn main() {} |
Sorry, I didn't see the other snippet :( |
cc @nikomatsakis? |
@pietroalbini : No problem |
Seems like the second issue is a bug. I'll file it and dig in a bit soon. |
Ok so I dug into this error a bit. The cause is the ordering of this "tail bit" in the MIR:
Here,
So, MIR borrowck is not wrong to complain -- the destructor is indeed running with access to memory whose storage is dead. The question is more why the data is getting generated in this way. At first glance, the order looks wrong there, but the lifetime of temporaries in the tail expression of a block has some subtle points, and I'm fairly sure this is intentional (though I wonder if we could alter it) cc @pnkfelix -- do you remember? |
The other question is why non-MIR-based borrowck accepts this code |
Nominated for Lang Team discussion. It's not clear what is best way forward, but any change to the underlying MIR is changing runtime semantics, which is something we should do only with great caution. That said, I think that the precise of temporaries being dropped here seems suboptimal. Maybe a lint is a good place to start -- which might also help us to "scope" how big of an impact a tweak here would have. |
For what it's worth, the playground linked in my previous comment causes an ICE in nightly (2018-04-01). |
This may be the same issue (lifetime error in stable and ICE in nightly). Note that adding a semicolon after // #![feature(nll)]
struct Foo<'a>(&'a i32);
impl<'a> Drop for Foo<'a> {
fn drop(&mut self) {}
}
impl<'a> Foo<'a> {
fn foo(&self) {}
}
fn bar() {
let i = 3;
Foo(&i).foo()
}
fn main() {
bar();
} |
I believe the ICE may be fixed by @spastorino's PR #49808 |
Yes, this is fixed by #49808 The output for your code is ...
|
Isn't this also a case of the drop check rule in action? c.f. #22321 |
Is this the same bug?
|
This issue was discussed (or at least alluded to) in today's compiler team meeting. I'm going to treat addressing it as part of overall potential NLL work, so I'm |
Visited for T-compiler triage. Removing NLL-fixed-by-NLL issues from the Release milestone, but continue to leave them open as issues themselves. That seems like it strikes the right balance here. |
At this point I think the diagnostic improvements added by NLL are all that particular feature (NLL) is going to do on its own to improve things here. There's obviously still some potential work here to be done, in any of the following areas:
See also #37612 for what I believe to be an instance of this bug. The comment thread there is well-worth reading; it includes arguments for why one might change the temporary lifetime semantics, but it also includes examples of why we want the semantics we have today. Having said that, since I don't think we're going to see further movement on this issue via NLL, I'm going to remove A-NLL from this issue. |
The current output for the code in the first comment is:
It suggests exactly one of the valid fixes. @rust-lang/wg-diagnostics do we think we can add any improvements here? @pnkfelix it feels to me that this could be closed now, right? |
@estebank yes, at this point I think the diagnostics are as good as we might expect them to get (and they are much improved over what this bug was reporting when it was originally filed). The main movement I could imagine at this point would be to actually change the dynamic semantics to try to make them have temporary lifetimes that are more "intuitive." But that would be a pretty serious (and probably breaking, depending on whether it changes when destructors run) change. Since thread on this issue is not particularly enlightening with regards to such a hypothetical change (compared to issues such as #15023, #37612, or this summary comment on #21114), and therefore I do not see value in leaving this issue open. Closing as fixed. |
UPDATE: The main remaining issue seems to be that the NLL checker is correctly identifying cases where temporary lifetimes are acting in surprising ways -- some of these were overlooked by the borrow checker. The only fix is to adjust the temporary lifetime rules, which would be technically backwards incompatible, but maybe something we could get away with. See this comment and follow-ups for more details. -- @nikomatsakis
See the second code snippet in comment below.
Old text
In the following code,
f
compiles butg
does not. It looks like the borrow ofa
gets a different lifetime between these 2 functions.Playground
The text was updated successfully, but these errors were encountered: