-
Notifications
You must be signed in to change notification settings - Fork 1
Add subsequence and slice split operations #164
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
base: claude/seq-offset-field
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -239,6 +239,17 @@ mod thrust_models { | |
| unimplemented!() | ||
| } | ||
|
|
||
| #[allow(dead_code)] | ||
| #[thrust::def::seq_subsequence] | ||
| #[thrust::ignored] | ||
| pub fn subsequence<U, V>(self, _start: U, _end: V) -> Self | ||
| where | ||
| U: super::Model<Ty = Int>, | ||
| V: super::Model<Ty = Int>, | ||
| { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| #[allow(dead_code)] | ||
| #[thrust::def::seq_concat] | ||
| #[thrust::ignored] | ||
|
|
@@ -961,6 +972,87 @@ fn _extern_spec_slice_last_mut<T>(slice: &mut [T]) -> Option<&mut T> | |
| <[T]>::last_mut(slice) | ||
| } | ||
|
|
||
| #[thrust::extern_spec_fn] | ||
| #[thrust_macros::requires(true)] | ||
| #[thrust_macros::ensures( | ||
| ((*slice).length > 0 | ||
| && result == Some(( | ||
| &(*slice).array[(*slice).offset], | ||
| &(*slice).subsequence(1, (*slice).length), | ||
| )) | ||
| ) | ||
| || ((*slice).length == 0 && result == None) | ||
| )] | ||
| fn _extern_spec_slice_split_first<T>(slice: &[T]) -> Option<(&T, &[T])> | ||
| where T: thrust_models::Model, T::Ty: PartialEq | ||
| { | ||
| <[T]>::split_first(slice) | ||
| } | ||
|
|
||
| #[thrust::extern_spec_fn] | ||
| #[thrust_macros::requires(true)] | ||
| #[thrust_macros::ensures( | ||
| ((*slice).length > 0 | ||
| && result == Some(( | ||
| &(*slice).array[(*slice).offset + (*slice).length - 1], | ||
| &(*slice).subsequence(0, (*slice).length - 1), | ||
| )) | ||
| ) | ||
| || ((*slice).length == 0 && result == None) | ||
| )] | ||
| fn _extern_spec_slice_split_last<T>(slice: &[T]) -> Option<(&T, &[T])> | ||
| where T: thrust_models::Model, T::Ty: PartialEq | ||
| { | ||
| <[T]>::split_last(slice) | ||
| } | ||
|
|
||
| #[thrust::extern_spec_fn] | ||
| #[thrust_macros::requires(true)] | ||
| #[thrust_macros::ensures( | ||
| ((*slice).length > 0 | ||
| && (!slice).offset == (*slice).offset | ||
| && (!slice).length == (*slice).length | ||
| && result == Some(( | ||
| thrust_models::model::Mut::new((*slice)[0], (!slice)[0]), | ||
| thrust_models::model::Mut::new( | ||
| (*slice).subsequence(1, (*slice).length), | ||
| (!slice).subsequence(1, (!slice).length), | ||
| ), | ||
|
Comment on lines
+1016
to
+1020
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a caller writes through a reference returned by Useful? React with 👍 / 👎. |
||
| )) | ||
| ) | ||
| || ((*slice).length == 0 && result == None && !slice == *slice) | ||
| )] | ||
| fn _extern_spec_slice_split_first_mut<T>(slice: &mut [T]) -> Option<(&mut T, &mut [T])> | ||
| where T: thrust_models::Model, T::Ty: PartialEq | ||
| { | ||
| <[T]>::split_first_mut(slice) | ||
| } | ||
|
|
||
| #[thrust::extern_spec_fn] | ||
| #[thrust_macros::requires(true)] | ||
| #[thrust_macros::ensures( | ||
| ((*slice).length > 0 | ||
| && (!slice).offset == (*slice).offset | ||
| && (!slice).length == (*slice).length | ||
| && result == Some(( | ||
| thrust_models::model::Mut::new( | ||
| (*slice)[(*slice).length - 1], | ||
| (!slice)[(!slice).length - 1], | ||
| ), | ||
| thrust_models::model::Mut::new( | ||
| (*slice).subsequence(0, (*slice).length - 1), | ||
| (!slice).subsequence(0, (!slice).length - 1), | ||
| ), | ||
| )) | ||
| ) | ||
| || ((*slice).length == 0 && result == None && !slice == *slice) | ||
| )] | ||
| fn _extern_spec_slice_split_last_mut<T>(slice: &mut [T]) -> Option<(&mut T, &mut [T])> | ||
| where T: thrust_models::Model, T::Ty: PartialEq | ||
| { | ||
| <[T]>::split_last_mut(slice) | ||
| } | ||
|
|
||
| // TODO: The following specs for Index/IndexMut methods are too specific; we should write specs for | ||
| // a generic index (I: SliceIndex) that isn't specific to usize, maybe once #83 is implemented. | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| //@error-in-other-file: Unsat | ||
| //@compile-flags: -C debug-assertions=off | ||
| #[thrust::trusted] | ||
| #[thrust_macros::requires(true)] | ||
| #[thrust_macros::ensures( | ||
| (*result).length == 2 | ||
| && (*result).array[(*result).offset] == 10 | ||
| && (*result).array[(*result).offset + 1] == 20 | ||
| )] | ||
| fn slice() -> &'static [i32] { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn main() { | ||
| let slice = slice(); | ||
| let (first, _tail) = slice.split_first().unwrap(); | ||
| // wrong: the first element is 10, not 11 | ||
| assert!(*first == 11); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| //@error-in-other-file: Unsat | ||
| //@compile-flags: -C debug-assertions=off | ||
| #[thrust::trusted] | ||
| #[thrust_macros::requires(true)] | ||
| #[thrust_macros::ensures( | ||
| (*result).length == 2 | ||
| && (*result).array[(*result).offset] == 10 | ||
| && (*result).array[(*result).offset + 1] == 20 | ||
| )] | ||
| fn slice() -> &'static mut [i32] { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn main() { | ||
| let slice = slice(); | ||
| let (first, _tail) = slice.split_first_mut().unwrap(); | ||
| // wrong: the first element is 10, not 11 | ||
| assert!(*first == 11); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| //@error-in-other-file: Unsat | ||
|
Check failure on line 1 in tests/ui/fail/slice_split_first_mut_mutation_unsound.rs
|
||
| //@compile-flags: -C debug-assertions=off | ||
|
|
||
| // KNOWN LIMITATION: mutation through a `&mut` returned inside a tuple (as | ||
| // `split_first_mut` does) does not currently propagate back to the original | ||
| // slice, which is unsound. This test is expected to be rejected (`Unsat`) once | ||
| // that is fixed; it is kept here as a reproduction until the fix lands. | ||
| #[thrust::trusted] | ||
| #[thrust_macros::requires(true)] | ||
| #[thrust_macros::ensures( | ||
| (*result).length == 2 | ||
| && (*result).array[(*result).offset] == 10 | ||
| && (*result).array[(*result).offset + 1] == 20 | ||
| )] | ||
| fn slice() -> &'static mut [i32] { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn main() { | ||
| let slice = slice(); | ||
| { | ||
| // Destructure the returned tuple, then mutate through its first reference. | ||
| let (first, _tail) = slice.split_first_mut().unwrap(); | ||
| *first = 11; | ||
| } | ||
| // The mutation makes slice[0] equal to 11, so this must be rejected. | ||
| assert!(slice[0] == 12); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| //@error-in-other-file: Unsat | ||
| //@compile-flags: -C debug-assertions=off | ||
| #[thrust::trusted] | ||
| #[thrust_macros::requires(true)] | ||
| #[thrust_macros::ensures( | ||
| (*result).length == 2 | ||
| && (*result).array[(*result).offset] == 10 | ||
| && (*result).array[(*result).offset + 1] == 20 | ||
| )] | ||
| fn slice() -> &'static [i32] { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn main() { | ||
| let slice = slice(); | ||
| let (last, _init) = slice.split_last().unwrap(); | ||
| // wrong: the last element is 20, not 21 | ||
| assert!(*last == 21); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| //@error-in-other-file: Unsat | ||
| //@compile-flags: -C debug-assertions=off | ||
| #[thrust::trusted] | ||
| #[thrust_macros::requires(true)] | ||
| #[thrust_macros::ensures( | ||
| (*result).length == 2 | ||
| && (*result).array[(*result).offset] == 10 | ||
| && (*result).array[(*result).offset + 1] == 20 | ||
| )] | ||
| fn slice() -> &'static mut [i32] { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn main() { | ||
| let slice = slice(); | ||
| let (last, _init) = slice.split_last_mut().unwrap(); | ||
| // wrong: the last element is 20, not 21 | ||
| assert!(*last == 21); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| //@check-pass | ||
| //@compile-flags: -C debug-assertions=off | ||
| #[thrust::trusted] | ||
| #[thrust_macros::requires(true)] | ||
| #[thrust_macros::ensures( | ||
| (*result).length == 2 | ||
| && (*result).array[(*result).offset] == 10 | ||
| && (*result).array[(*result).offset + 1] == 20 | ||
| )] | ||
| fn slice() -> &'static [i32] { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn main() { | ||
| let slice = slice(); | ||
| let (first, tail) = slice.split_first().unwrap(); | ||
| assert!(*first == 10); | ||
| assert!(tail.len() == 1); | ||
| assert!(*tail.first().unwrap() == 20); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| //@check-pass | ||
| //@compile-flags: -C debug-assertions=off | ||
| #[thrust::trusted] | ||
| #[thrust_macros::requires(true)] | ||
| #[thrust_macros::ensures( | ||
| (*result).length == 2 | ||
| && (*result).array[(*result).offset] == 10 | ||
| && (*result).array[(*result).offset + 1] == 20 | ||
| )] | ||
| fn slice() -> &'static mut [i32] { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn main() { | ||
| let slice = slice(); | ||
| let (first, tail) = slice.split_first_mut().unwrap(); | ||
| assert!(*first == 10); | ||
| assert!(tail.len() == 1); | ||
| assert!(*tail.first().unwrap() == 20); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| //@check-pass | ||
| //@compile-flags: -C debug-assertions=off | ||
| #[thrust::trusted] | ||
| #[thrust_macros::requires(true)] | ||
| #[thrust_macros::ensures( | ||
| (*result).length == 2 | ||
| && (*result).array[(*result).offset] == 10 | ||
| && (*result).array[(*result).offset + 1] == 20 | ||
| )] | ||
| fn slice() -> &'static [i32] { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn main() { | ||
| let slice = slice(); | ||
| let (last, init) = slice.split_last().unwrap(); | ||
| assert!(*last == 20); | ||
| assert!(init.len() == 1); | ||
| assert!(*init.first().unwrap() == 10); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| //@check-pass | ||
| //@compile-flags: -C debug-assertions=off | ||
| #[thrust::trusted] | ||
| #[thrust_macros::requires(true)] | ||
| #[thrust_macros::ensures( | ||
| (*result).length == 2 | ||
| && (*result).array[(*result).offset] == 10 | ||
| && (*result).array[(*result).offset + 1] == 20 | ||
| )] | ||
| fn slice() -> &'static mut [i32] { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn main() { | ||
| let slice = slice(); | ||
| let (last, init) = slice.split_last_mut().unwrap(); | ||
| assert!(*last == 20); | ||
| assert!(init.len() == 1); | ||
| assert!(*init.first().unwrap() == 10); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When compiling any of the new split specs that call
slice.subsequence(...)or(*slice).subsequence(...), this by-value receiver tries to move theSeqout of a shared reference orMutderef in the generated formula function. SinceSeqis notCopy, those extern specs fail type checking before verification; make this helper take&self(aslendoes) or otherwise avoid moving the receiver.Useful? React with 👍 / 👎.