Skip to content

Commit

Permalink
Merge pull request #5908 from roc-lang/miri-fixes
Browse files Browse the repository at this point in the history
roc_std: fixes found by running miri
  • Loading branch information
rtfeldman authored Oct 16, 2023
2 parents ff6586b + f717cb7 commit 54c4ddd
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 37 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ perfcnt = "0.8.0"
pest = "2.5.6"
pest_derive = "2.5.6"
pretty_assertions = "1.3.0" # update roc_std/Cargo.toml on change
proc-macro2 = "1.0.51"
proc-macro2 = "1.0.63"
proptest = "1.1.0"
pulldown-cmark = { version = "0.9.2", default-features = false }
quickcheck = "1.0.3" # update roc_std/Cargo.toml on change
Expand Down
26 changes: 26 additions & 0 deletions crates/roc_std/src/roc_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -829,4 +829,30 @@ mod tests {
drop(a);
drop(b);
}

#[test]
fn readonly_list_is_sendsafe() {
let x = RocList::from_slice(&[1, 2, 3, 4, 5]);
unsafe { x.set_readonly() };
assert!(x.is_readonly());

let y = x.clone();
let z = y.clone();

let safe_x = SendSafeRocList::from(x);
let new_x = RocList::from(safe_x);
assert!(new_x.is_readonly());
assert!(y.is_readonly());
assert!(z.is_readonly());
assert_eq!(new_x.as_slice(), &[1, 2, 3, 4, 5]);

let ptr = new_x.ptr_to_allocation();

drop(y);
drop(z);
drop(new_x);

// free the underlying memory
unsafe { crate::roc_dealloc(ptr, std::mem::align_of::<usize>() as u32) }
}
}
13 changes: 7 additions & 6 deletions crates/roc_std/src/roc_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,9 @@ impl RocStr {
}
}
RocStrInnerRef::SmallString(small_str) => {
let mut bytes = small_str.bytes;
let mut bytes = [0; size_of::<RocList<u8>>()];
let mut it = small_str.bytes.iter();
bytes = bytes.map(|_| it.next().copied().unwrap_or_default());

// Even if the small string is at capacity, there will be room to write
// a terminator in the byte that's used to store the length.
Expand Down Expand Up @@ -380,9 +382,7 @@ impl RocStr {
self.with_terminator(terminator, |dest_ptr: *mut u16, str_slice: &str| {
// Translate UTF-8 source bytes into UTF-16 and write them into the destination.
for (index, wchar) in str_slice.encode_utf16().enumerate() {
unsafe {
*(dest_ptr.add(index)) = wchar;
}
unsafe { std::ptr::write_unaligned(dest_ptr.add(index), wchar) };
}

func(dest_ptr, str_slice.len())
Expand Down Expand Up @@ -467,7 +467,7 @@ impl RocStr {
use core::mem::align_of;

let terminate = |alloc_ptr: *mut E, str_slice: &str| unsafe {
*(alloc_ptr.add(str_slice.len())) = terminator;
std::ptr::write_unaligned(alloc_ptr.add(str_slice.len()), terminator);

func(alloc_ptr, str_slice)
};
Expand Down Expand Up @@ -548,7 +548,8 @@ impl RocStr {
let available_bytes = size_of::<SmallString>();

if needed_bytes < available_bytes {
terminate(small_str.bytes.as_ptr() as *mut E, self.as_str())
let mut bytes = small_str.bytes;
terminate(&mut bytes as *mut u8 as *mut E, self.as_str())
} else {
fallback(self.as_str())
}
Expand Down
45 changes: 17 additions & 28 deletions crates/roc_std/tests/test_roc_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut

#[cfg(test)]
mod test_roc_std {
use roc_std::{RocBox, RocDec, RocList, RocResult, RocStr, SendSafeRocList, SendSafeRocStr};
use roc_std::{RocBox, RocDec, RocList, RocResult, RocStr, SendSafeRocStr};

fn roc_str_byte_representation(string: &RocStr) -> [u8; RocStr::SIZE] {
unsafe { core::mem::transmute_copy(string) }
Expand Down Expand Up @@ -341,39 +341,22 @@ mod test_roc_std {
let x = RocStr::from("short");
let y = x.clone();
let z = y.clone();
assert_eq!(x.is_unique(), true);
assert_eq!(y.is_unique(), true);
assert_eq!(z.is_unique(), true);
assert!(x.is_unique());
assert!(y.is_unique());
assert!(z.is_unique());

let safe_x = SendSafeRocStr::from(x);
let new_x = RocStr::from(safe_x);
assert_eq!(new_x.is_unique(), true);
assert_eq!(y.is_unique(), true);
assert_eq!(z.is_unique(), true);
assert!(new_x.is_unique());
assert!(y.is_unique(),);
assert!(z.is_unique(),);
assert_eq!(new_x.as_str(), "short");
}

#[test]
fn empty_list_is_unique() {
let roc_list = RocList::<RocStr>::empty();
assert_eq!(roc_list.is_unique(), true);
}

#[test]
fn readonly_list_is_sendsafe() {
let x = RocList::from_slice(&[1, 2, 3, 4, 5]);
unsafe { x.set_readonly() };
assert_eq!(x.is_readonly(), true);

let y = x.clone();
let z = y.clone();

let safe_x = SendSafeRocList::from(x);
let new_x = RocList::from(safe_x);
assert_eq!(new_x.is_readonly(), true);
assert_eq!(y.is_readonly(), true);
assert_eq!(z.is_readonly(), true);
assert_eq!(new_x.as_slice(), &[1, 2, 3, 4, 5]);
assert!(roc_list.is_unique());
}
}

Expand Down Expand Up @@ -413,12 +396,18 @@ mod with_terminator {
// utf16_nul_terminated
{
let answer = roc_str.utf16_nul_terminated(|ptr, len| {
let bytes: &[u16] = unsafe { slice::from_raw_parts(ptr.cast(), len + 1) };
let bytes: &[u8] = unsafe { slice::from_raw_parts(ptr as *mut u8, 2 * (len + 1)) };

let items: Vec<u16> = bytes
.chunks(2)
.map(|c| c.try_into().unwrap())
.map(u16::from_ne_bytes)
.collect();

// Verify that it's nul-terminated
assert_eq!(bytes[len], 0);
assert_eq!(items[len], 0);

let string = String::from_utf16(&bytes[0..len]).unwrap();
let string = String::from_utf16(&items[0..len]).unwrap();

assert_eq!(string.as_str(), string);

Expand Down

0 comments on commit 54c4ddd

Please sign in to comment.