-
Notifications
You must be signed in to change notification settings - Fork 275
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
MIRI errors for all tests on chapter 6 #213
Comments
Changing it to use Option<NonNull> instead of a Box seems to fix them (With and without MIRIFLAGS): #![allow(unused)]
use std::ptr::{self, NonNull};
pub struct List<T> {
head: Link<T>,
tail: Option<NonNull<Node<T>>>,
}
type Link<T> = Option<NonNull<Node<T>>>;
struct Node<T> {
elem: T,
next: Link<T>,
}
impl<T> List<T> {
pub fn new() -> Self {
List {
head: None,
tail: None,
}
}
pub fn push(&mut self, elem: T) {
let mut new_tail = Box::new(Node {
elem: elem,
next: None,
});
let raw_tail: *mut _ = Box::into_raw(new_tail);
let raw_tail = unsafe { NonNull::new_unchecked(raw_tail) };
let raw_tail = Some(raw_tail);
if let Some(tail) = self.tail {
unsafe {
(*tail.as_ptr()).next = raw_tail;
}
} else {
self.head = raw_tail;
}
self.tail = raw_tail;
}
pub fn pop(&mut self) -> Option<T> {
self.head.take().map(|head| {
let head = *unsafe { Box::from_raw(head.as_ptr()) };
self.head = head.next;
if self.head.is_none() {
self.tail = None;
}
head.elem
})
}
pub fn peek(&self) -> Option<&T> {
self.head
.as_ref()
.map(|node| unsafe { &(*node.as_ptr()).elem })
}
pub fn peek_mut(&mut self) -> Option<&mut T> {
self.head
.as_mut()
.map(|node| unsafe { &mut (*node.as_ptr()).elem })
}
pub fn into_iter(self) -> IntoIter<T> {
IntoIter(self)
}
pub fn iter(&self) -> Iter<'_, T> {
Iter {
next: self.head.map(|ptr| unsafe { &*ptr.as_ptr() }),
}
}
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
IterMut {
next: self.head.map(|ptr| unsafe { &mut *ptr.as_ptr() }),
}
}
}
pub struct IntoIter<T>(List<T>);
pub struct Iter<'a, T> {
next: Option<&'a Node<T>>,
}
pub struct IterMut<'a, T> {
next: Option<&'a mut Node<T>>,
}
impl<T> Drop for List<T> {
fn drop(&mut self) {
let mut cur_link = self.head.take();
while let Some(node_ptr) = cur_link {
unsafe {
cur_link = (*node_ptr.as_ptr()).next;
Box::from_raw(node_ptr.as_ptr());
}
}
}
}
impl<T> Iterator for IntoIter<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.0.pop()
}
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.next.map(|node| {
self.next = unsafe { node.next.map(|x| &*x.as_ptr()) };
&node.elem
})
}
}
impl<'a, T> Iterator for IterMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
self.next.take().map(|node| {
self.next = unsafe { node.next.map(|x| &mut *x.as_ptr()) };
&mut node.elem
})
}
}
mod test {
use super::List;
#[test]
pub fn basics() {
let mut list = List::new();
// Check empty list behaves right
assert_eq!(list.pop(), None);
// Populate list
list.push(1);
list.push(2);
list.push(3);
// Check normal removal
assert_eq!(list.pop(), Some(1));
assert_eq!(list.pop(), Some(2));
// Push some more just to make sure nothing's corrupted
list.push(4);
list.push(5);
// Check normal removal
assert_eq!(list.pop(), Some(3));
assert_eq!(list.pop(), Some(4));
// Check exhaustion
assert_eq!(list.pop(), Some(5));
assert_eq!(list.pop(), None);
// Check the exhaustion case fixed the pointer right
list.push(6);
list.push(7);
// Check normal removal
assert_eq!(list.pop(), Some(6));
assert_eq!(list.pop(), Some(7));
assert_eq!(list.pop(), None);
}
#[test]
pub fn into_iter() {
let mut list = List::new();
list.push(1);
list.push(2);
list.push(3);
let mut iter = list.into_iter();
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(3));
assert_eq!(iter.next(), None);
}
#[test]
pub fn iter() {
let mut list = List::new();
list.push(1);
list.push(2);
list.push(3);
let mut iter = list.iter();
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&3));
assert_eq!(iter.next(), None);
}
#[test]
pub fn iter_mut() {
let mut list = List::new();
list.push(1);
list.push(2);
list.push(3);
let mut iter = list.iter_mut();
assert_eq!(iter.next(), Some(&mut 1));
assert_eq!(iter.next(), Some(&mut 2));
assert_eq!(iter.next(), Some(&mut 3));
assert_eq!(iter.next(), None);
}
}
fn main() {
} My understanding is the Box is marked noalias which can't alias with the tail pointer. |
Yeah, this is an annoying problem. This chapter was written in a time when we were far more wobbly around the pointer semantics of Rust, and this code was considered "fine". Alas, it is not. |
fixed in #215 😎 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Running
cargo +nightly miri test
gives errors for all the tests:Error on basics with
MIRIFLAGS="-Zmiri-symbolic-alignment-check -Zmiri-check-number-validity -Zmiri-tag-raw-pointers"
:That error is the same with those MIRIFLAGS for all tests.
Error on basics with no MIRIFLAGS:
Error on into_iter with no MIRIFLAGS:
Error on iter with no MIRIFLAGS:
Error on iter_mut with no MIRIFLAGS:
Possibly related: rust-lang/unsafe-code-guidelines#133
The text was updated successfully, but these errors were encountered: