-
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
Bug: nested closure outlives borrowed value. #53432
Comments
Can you provide a even smaller example at best without any external dependy? ❤️ (that's a very good bug you found!) |
pub trait Future {
fn run(self);
}
impl<F> Future for F where F: FnOnce() {
fn run(self) {
self();
}
}
pub trait Action {
type Output: Future;
fn run(self) -> Self::Output;
}
impl<T: Future, F: FnOnce() -> T> Action for F {
type Output = T;
fn run(self) -> Self::Output {
self()
}
}
fn retry<A: Action>(action: A) -> impl Future {
action.run()
}
struct Core<F: Future> {
vec: Vec<F>,
}
impl<F: Future> Core<F> {
pub fn spawn(&mut self, f: F) where F: Future + 'static {
self.vec.push(f);
}
pub fn run(self) {
for f in self.vec.into_iter() {
f.run()
};
}
}
/*
The `nested` closure avoids the check of its lifetime here, if:
- the `nasted` closure is nested into the `action` closure, and
- the `action` closure is passed into the `retry` function, and
- the `retry` function take a generic by the `Action` trait argument, and
- the `Action` trait is implemented for an `Fn*` trait.
As a result, we get arbitrary values in variables and at best SIGSEGV.
*/
fn main() {
let mut core = Core { vec: Vec::new() };
for i in &[1, 2, 3, 4, 5] {
println!("outer: {}", i);
let f = move || {
println!("inner: {}", i);
};
let action = move || {
|| f() // The `nested` closure
};
core.spawn(retry(action));
}
core.run();
} Output:
Errors:
|
Simplified: pub trait Action {
type Output: FnOnce();
fn run(self) -> Self::Output;
}
impl<T: FnOnce(), F: FnOnce() -> T> Action for F {
type Output = T;
fn run(self) -> Self::Output {
self()
}
}
fn retry<A: Action>(action: A) -> impl FnOnce() {
action.run()
}
//Note: when specifying the associate type explicitly, the borrow checker complained.
//fn retry<F:FnOnce(),A: Action<Output=F>>(action: A) -> impl FnOnce() {
// action.run()
//}
/*
The `nested` closure avoids the check of its lifetime here, if:
- the `nasted` closure is nested into the `action` closure, and
- the `action` closure is passed into the `retry` function, and
- the `retry` function take a generic by the `Action` trait argument, and
- the `Action` trait is implemented for an `Fn*` trait.
As a result, we get arbitrary values in variables and at best SIGSEGV.
*/
fn main() {
let mut core = Vec::new();
for i in &[1, 2, 3, 4, 5] {
println!("outer: {}", i);
let f = move || {
println!("inner: {}", i);
};
let action = move || {
|| f() // The `nested` closure
};
core.push(retry(action));
}
(core.remove(0)())
} The above prints random numbers (no segfault). |
@earthengine This is how an undefined behavior works. It depends on many factors. |
So, what should we do about this one? Do we just wait until it will become a hard fault in X relaeses? Or should it get fixed? |
It is already a future incompat warning on the edition. We'll get NLL on the 2015 editon at some point, too. So I presume we'll just wait until then. |
Also there is a way to use NLL checks with the nightly, it's enough to catch such bugs. |
NLL (migrate mode) is enabled in all editions as of PR #59114. The only policy question is that, under migrate mode, we only emit a warning on this (unsound) test case. Therefore, I am not 100% sure whether we should close this until that warning has been turned into a hard-error under our (still in development) future-compatibility lint policy. |
…hewjasper Rust 2015: No longer downgrade NLL errors As per decision on a language team meeting as described in rust-lang#63565 (comment), in Rust 2015, we refuse to downgrade NLL errors, that AST borrowck accepts, into warnings and keep them as hard errors. The remaining work to throw out AST borrowck and adjust some tests still remains after this PR. Fixes rust-lang#38899 Fixes rust-lang#53432 Fixes rust-lang#45157 Fixes rust-lang#31567 Fixes rust-lang#27868 Fixes rust-lang#47366 r? @matthewjasper
…hewjasper Rust 2015: No longer downgrade NLL errors As per decision on a language team meeting as described in rust-lang#63565 (comment), in Rust 2015, we refuse to downgrade NLL errors, that AST borrowck accepts, into warnings and keep them as hard errors. The remaining work to throw out AST borrowck and adjust some tests still remains after this PR. Fixes rust-lang#38899 Fixes rust-lang#53432 Fixes rust-lang#45157 Fixes rust-lang#31567 Fixes rust-lang#27868 Fixes rust-lang#47366 r? @matthewjasper
…hewjasper Rust 2015: No longer downgrade NLL errors As per decision on a language team meeting as described in rust-lang#63565 (comment), in Rust 2015, we refuse to downgrade NLL errors, that AST borrowck accepts, into warnings and keep them as hard errors. The remaining work to throw out AST borrowck and adjust some tests still remains after this PR. Fixes rust-lang#38899 Fixes rust-lang#53432 Fixes rust-lang#45157 Fixes rust-lang#31567 Fixes rust-lang#27868 Fixes rust-lang#47366 r? @matthewjasper
(Playground)
Output:
Errors:
This emits a warning in the 2018 edition mode, but silently accepts code leading to UB in the 2015 edition.
The text was updated successfully, but these errors were encountered: