forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit is a fix for rust-lang#48493 where calling `Weak::new` where `T` is an uninhabited type would segfault. The cause for this issue was pretty subtle and the fix here is mostly a holdover until rust-lang#47650 is implemented. The `Weak<!>` struct internally contains a `NonNull<RcBox<!>>`. The `RcBox<!>` type is uninhabited, however, as it directly embeds the `!` type. Consequently the size of `RcBox<!>` is zero, which means that `NonNull<RcBox<!>>` always contains a pointer with a value of 1. Currently all boxes of zero-sized-types are actually pointers to the address 1 (as they shouldn't be read anyway). The problem comes about when later on we have a method called `Weak::inner` which previously returned `&RcBox<T>`. This was actually invalid because the instance of `&RcBox<T> ` never existed (only the uninitialized part). This means that when we try to actually modify `&RcBox`'s weak count in the destructor for `Weak::new` we're modifying the address 1! This ends up causing a segfault. This commit takes the strategy of modifying the `Weak::inner` method to return an `Option<&RcBox<T>>` that is `None` whenever the size of `RcBox<T>` is 0 (so it couldn't actually exist). This does unfortunately add more dispatch code to operations like `Weak<Any>::clone`. Eventually the "correct" fix for this is to have `RcBox<T>` store a union of `T` and a ZST like `()`, and that way the size of `RcBox<T>` will never be 0 and we won't need this branch. Until rust-lang#47650 is implemented, though, we can't use `union`s and unsized types together. Closes rust-lang#48493
- Loading branch information
1 parent
ab8b961
commit 06cd4cf
Showing
3 changed files
with
146 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
use std::collections::HashMap; | ||
use std::collections::BTreeMap; | ||
|
||
#[derive(Eq, PartialEq, Hash, PartialOrd, Ord)] | ||
enum Void {} | ||
|
||
trait Foo {} | ||
|
||
impl<T> Foo for T {} | ||
|
||
fn main() { | ||
std::rc::Weak::<Void>::new(); | ||
std::rc::Weak::<Void>::new().clone(); | ||
(std::rc::Weak::<Void>::new() as std::rc::Weak<Foo>); | ||
(std::rc::Weak::<Void>::new() as std::rc::Weak<Foo>).clone(); | ||
std::sync::Weak::<Void>::new(); | ||
(std::sync::Weak::<Void>::new() as std::sync::Weak<Foo>); | ||
(std::sync::Weak::<Void>::new() as std::sync::Weak<Foo>).clone(); | ||
|
||
let mut h: HashMap<Void, Void> = HashMap::new(); | ||
assert_eq!(h.len(), 0); | ||
assert_eq!(h.iter().count(), 0); | ||
assert_eq!(h.iter_mut().count(), 0); | ||
assert_eq!(h.into_iter().count(), 0); | ||
|
||
let mut h: BTreeMap<Void, Void> = BTreeMap::new(); | ||
assert_eq!(h.len(), 0); | ||
assert_eq!(h.iter().count(), 0); | ||
assert_eq!(h.iter_mut().count(), 0); | ||
assert_eq!(h.into_iter().count(), 0); | ||
|
||
let mut h: Vec<Void> = Vec::new(); | ||
assert_eq!(h.len(), 0); | ||
assert_eq!(h.iter().count(), 0); | ||
assert_eq!(h.iter_mut().count(), 0); | ||
assert_eq!(h.into_iter().count(), 0); | ||
} |