Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upborrow scopes should not always be lexical #6393
Comments
ghost
assigned
nikomatsakis
May 10, 2013
This comment has been minimized.
This comment has been minimized.
|
nominating for production ready |
This comment has been minimized.
This comment has been minimized.
|
I would call this either well-defined or backwards-compat. |
This comment has been minimized.
This comment has been minimized.
|
Examples from Jack [09:57:06] https://github.com/mozilla/servo/blob/master/src/components/servo/layout/box_builder.rs#L387-L411 |
This comment has been minimized.
This comment has been minimized.
|
The code got reorganized, but here is the specific appeasement of the borrow checker i had to do: |
nikomatsakis
referenced this issue
May 20, 2013
Closed
Borrow checker is confused by return statements #6613
This comment has been minimized.
This comment has been minimized.
|
After some discussion, it's clear that the real problem here is that the borrow checker makes no effort to track aliases, but rather always relies on the region system to determine when a borrow goes out of scope. I am reluctant to change this, at least not in the short term, because there are a number of other outstanding issues I'd like to address first and any changes would be a significant modification to the borrow checker. See issue #6613 for another related example and a somewhat detailed explanation. |
This comment has been minimized.
This comment has been minimized.
cscott
commented
May 20, 2013
|
I wonder if we could improve the error messages to make it more clear what's going on? Lexical scopes are relatively easy to understand, but in the examples of this issue that I've stumbled across it was by no means obvious what was going on. |
This comment has been minimized.
This comment has been minimized.
|
just a bug, removing milestone/nomination. |
This comment has been minimized.
This comment has been minimized.
|
accepted for far future milestone |
This comment has been minimized.
This comment has been minimized.
|
triage bump |
metajack
referenced this issue
Sep 9, 2013
Closed
Borrow checker reports multiple mutable borrows when there's only one #9068
This comment has been minimized.
This comment has been minimized.
|
I've had some thoughts about the best way to fix this. My basic plan is that we would have a notion of when a value "escapes". It will take some work to formalize that notion. Basically, when a borrowed pointer is created, we will then track whether it has escaped. When the pointer is dead, if it is has not escaped, this can be considered to kill the loan. This basic idea covers cases like "let p = &...; use-p-a-bit-but-never-again; expect-loan-to-be-expired-here;" Part of the analysis will be a rule that indicates when a return value that contains a borrowed pointer can be considered not to have escaped yet. This would cover cases like "match table.find(...) { ... None => { expect-table-not-to-be-loaned-here; } }" The most interesting part of all this is the escaping rules, of course. I think that the rules would have to take into account the formal definition of the function, and in particular to take advantage of the knowledge bound lifetimes give us. For example, most escape analyses would consider a pointer
then in fact we know that
So presumably the escaping rules would have to consider whether the bound lifetime appeared in a mutable location or not. This is effectively a form of type-based alias analysis. Similar reasoning I think applies to function return values. Hence
The reason here is that because Another way to think about this is not that the loan is expired early, but rather that the scope of a loan begins (typically) as tied to the borrowed variable and not the full lifetime, and is only promoted to the full lifetime when the variable escapes. Reborrows are a slight complication, but they can be handled in various ways. A reborrow is when you borrow the contents of a borrowed pointer -- they happen all the time because the compiler inserts them automatically into almost every method call. Consider a borowed pointer Anyway, more thought is warranted, but I'm starting to see how this could work. I'm still reluctant to embark on any extensions like this until #2202 and #8624 are fixed, those being the two known problems with borrowck. I'd also like to have more progress on a soundness proof before we go about extending the system. The other extension that is on the timeline is #6268. |
This comment has been minimized.
This comment has been minimized.
|
I believe I've run into this bug. My use-case and work-around attempts: |
nikomatsakis
referenced this issue
Nov 11, 2013
Closed
mut borrow checker seems to be too conservative in matches #10390
This comment has been minimized.
This comment has been minimized.
|
Here's another example of this bug (I think): use std::util;
enum List<T> {
Cons(T, ~List<T>),
Nil
}
fn find_mut<'a,T>(prev: &'a mut ~List<T>, pred: |&T| -> bool) -> Option<&'a mut ~List<T>> {
match prev {
&~Cons(ref x, _) if pred(x) => {}, // NB: can't return Some(prev) here
&~Cons(_, ref mut rs) => return find_mut(rs, pred),
&~Nil => return None
};
return Some(prev)
}I'd like to write: fn find_mut<'a,T>(prev: &'a mut ~List<T>, pred: |&T| -> bool) -> Option<&'a mut ~List<T>> {
match prev {
&~Cons(ref x, _) if pred(x) => return Some(prev),
&~Cons(_, ref mut rs) => return find_mut(rs, pred),
&~Nil => return None
}
}reasoning that the |
ezyang
referenced this issue
Dec 17, 2013
Closed
incorrect borrow conflict with temporary borrow #7211
This was referenced Feb 10, 2014
This comment has been minimized.
This comment has been minimized.
|
I've had more thoughts about how to code this up. My basic plan is that for each loan there would be two bits: an escaped version and a non-escaped version. Initially we add the non-escaped version. When a reference escapes, we add the escaped bits. When a variable (or temporary, etc) goes dead, we kill the non-escaped bits -- but leave the escaped bits (if set) untouched. I believe this covers all major examples. |
This comment has been minimized.
This comment has been minimized.
|
cc @flaper87 |
This comment has been minimized.
This comment has been minimized.
|
Does this issue cover this?
|
This comment has been minimized.
This comment has been minimized.
|
cc me |
nikomatsakis
referenced
this issue
in nrc/rust
Mar 18, 2014
This comment has been minimized.
This comment has been minimized.
|
Good examples in #9113 |
This comment has been minimized.
This comment has been minimized.
|
cc me |
huonw
referenced this issue
May 6, 2014
Closed
Borrow checker should exploit information provided by match alternatives #13956
This comment has been minimized.
This comment has been minimized.
|
I could be mistaken, but the following code seems to be hitting this bug as well:
Ideally, the mutable borrow caused by calling set_val would end as soon as the function returns. Note that removing the 'int_ref' field from the struct (and associated code) causes the issue to go away. The behavior is inconsistent. |
huonw
added
the
A-borrow-checker
label
Feb 3, 2015
This was referenced Feb 14, 2015
This comment has been minimized.
This comment has been minimized.
userxfce
commented
Apr 5, 2015
|
For the case: "let p = &...; use-p-a-bit-but-never-again; expect-loan-to-be-expired-here;" I would find acceptable for now a kill(p) instruction to manually declare end of scope for that borrow. Later versions could simply ignore this instruction if it is not needed or flag it as an error if re-use of p is detected after it. |
This comment has been minimized.
This comment has been minimized.
userxfce
commented
Apr 5, 2015
/* (wanted) */
/*
fn main() {
let mut x = 10;
let y = &mut x;
println!("x={}, y={}", x, *y);
*y = 11;
println!("x={}, y={}", x, *y);
}
*/
/* had to */
fn main() {
let mut x = 10;
{
let y = &x;
println!("x={}, y={}", x, *y);
}
{
let y = &mut x;
*y = 11;
}
let y = &x;
println!("x={}, y={}", x, *y);
} |
This comment has been minimized.
This comment has been minimized.
|
There's the drop() method in the prelude that does that. But doesn't seem On Sun, Apr 5, 2015, 1:41 PM axeoth notifications@github.com wrote:
|
pnkfelix
referenced this issue
Apr 16, 2015
Closed
Non-lexical borrow scopes and better treatment of nested method calls #811
This comment has been minimized.
This comment has been minimized.
|
Closing in favor of rust-lang/rfcs#811 |
nikomatsakis
closed this
Apr 16, 2015
bluss
referenced this issue
Jun 11, 2015
Closed
Call to FnMut closure borrows closure too early #26193
arielb1
referenced this issue
Sep 17, 2015
Closed
Mutable borrows in an `else if` after an `if let` are disallowed #28449
craftytrickster
referenced
this issue
in craftytrickster/servo
Jan 22, 2016
This comment has been minimized.
This comment has been minimized.
|
@metajack the link for your original code is a 404. Can you include it inline for people reading this bug? |
This comment has been minimized.
This comment has been minimized.
|
After some digging, I believe this is equivalent to the original code: |
This comment has been minimized.
This comment has been minimized.
|
Or rather, that's the workaround I used when I filed this bug. The original code before that change seems to be this: I'm not sure how relevant these specific examples are now since they were pre-Rust 1.0. |
This comment has been minimized.
This comment has been minimized.
|
@metajack it would be great to have an ultra simple (post 1.0) example in the top of this issue. This issue is now part of rust-lang/rfcs#811 |
This comment has been minimized.
This comment has been minimized.
fn main() {
let mut nums=vec![10i,11,12,13];
*nums.get_mut(nums.len()-2)=2;
} |
This comment has been minimized.
This comment has been minimized.
|
I think what I was complaining about was something like this: That particular case appears to work now. |
This comment has been minimized.
This comment has been minimized.
Wyverald
commented
Jan 11, 2017
|
@vitiral An example in today's Rust that I believe applies: fn main() {
let mut vec = vec!();
match vec.first() {
None => vec.push(5),
Some(v) => unreachable!(),
}
}The Curiously, if you don't try to capture |
This comment has been minimized.
This comment has been minimized.
|
@wyverland oh ya, I hit that just yesterday, pretty annoying. @metajack can you edit the first post to include that example? |
arielb1
referenced this issue
Jan 18, 2017
Open
borrowed referent of a `&T` sometimes incorrectly allowed #38899
Mark-Simulacrum
referenced this issue
May 15, 2017
Merged
Translate array drop glue using MIR #41917
arielb1
referenced this issue
May 16, 2017
Merged
introduce local-scope to prevent `StorageLive`/`StorageDead` in statics #42023
oli-obk
referenced this issue
Sep 6, 2017
Open
Meta issue for FIXMEs that reference closed issues #44366
This comment has been minimized.
This comment has been minimized.
|
It's not yet hit nightly, but I just want to say that this now compiles: #![feature(nll)]
fn main() {
let mut vec = vec!();
match vec.first() {
None => vec.push(5),
Some(v) => unreachable!(),
}
} |
metajack commentedMay 10, 2013
•
edited
If you borrow immutably in an
iftest, the borrow lasts for the wholeifexpression. This means that mutable borrows in the clauses will cause the borrow checker to fail.This can also happen when borrowing in the match expression, and needing a mutable borrow in one of the arms.
See here for an example where the if borrows boxes, which causes the nearest upwards
@mutto freeze. Thenremove_child()which needs to borrow mutably conflicts.https://github.com/mozilla/servo/blob/master/src/servo/layout/box_builder.rs#L387-L411
Updated example from @Wyverald