diff --git a/README.md b/README.md index 1cd9b8a3a2ae1..6f184e252182a 100644 --- a/README.md +++ b/README.md @@ -256,7 +256,7 @@ Also, you may find the [rustdocs for the compiler itself][rustdocs] useful. [rust-discord]: https://discord.gg/rust-lang [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/about-this-guide.html -[rustdocs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ +[rustdocs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ ## License diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index 414ac7e63a331..7ebd8054ba0b0 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -6,73 +6,78 @@ The tracking issue for this feature is: [#39699](https://github.com/rust-lang/ru This feature allows for use of one of following sanitizers: -* [AddressSanitizer][clang-asan] a faster memory error detector. Can - detect out-of-bounds access to heap, stack, and globals, use after free, use - after return, double free, invalid free, memory leaks. +* [AddressSanitizer][clang-asan] a fast memory error detector. * [LeakSanitizer][clang-lsan] a run-time memory leak detector. * [MemorySanitizer][clang-msan] a detector of uninitialized reads. * [ThreadSanitizer][clang-tsan] a fast data race detector. -To enable a sanitizer compile with `-Zsanitizer=...` option, where value is one -of `address`, `leak`, `memory` or `thread`. +To enable a sanitizer compile with `-Zsanitizer=address`, `-Zsanitizer=leak`, +`-Zsanitizer=memory` or `-Zsanitizer=thread`. Only a single sanitizer can be +enabled at a time. -# Examples +# AddressSanitizer -This sections show various issues that can be detected with sanitizers. For -simplicity, the examples are prepared under assumption that optimization level -used is zero. +AddressSanitizer is a memory error detector. It can detect the following types +of bugs: -## AddressSanitizer +* Out of bound accesses to heap, stack and globals +* Use after free +* Use after return (runtime flag `ASAN_OPTIONS=detect_stack_use_after_return=1`) +* Use after scope +* Double-free, invalid free +* Memory leaks + +AddressSanitizer is supported on the following targets: + +* `x86_64-apple-darwin` +* `x86_64-unknown-linux-gnu` + +AddressSanitizer works with non-instrumented code although it will impede its +ability to detect some bugs. It is not expected to produce false positive +reports. + +## Examples Stack buffer overflow: -```shell -$ cat a.rs +```rust fn main() { let xs = [0, 1, 2, 3]; let _y = unsafe { *xs.as_ptr().offset(4) }; } -$ rustc -Zsanitizer=address a.rs -$ ./a -================================================================= -==10029==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffcc15f43d0 at pc 0x55f77dc015c5 bp 0x7ffcc15f4390 sp 0x7ffcc15f4388 -READ of size 4 at 0x7ffcc15f43d0 thread T0 - #0 0x55f77dc015c4 in a::main::hab3bd2a745c2d0ac (/tmp/a+0xa5c4) - #1 0x55f77dc01cdb in std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::haa8c76d1faa7b7ca (/tmp/a+0xacdb) - #2 0x55f77dc90f02 in std::rt::lang_start_internal::_$u7b$$u7b$closure$u7d$$u7d$::hfeb9a1aef9ac820d /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/rt.rs:48:12 - #3 0x55f77dc90f02 in std::panicking::try::do_call::h12f0919717b8e0a6 /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panicking.rs:288:39 - #4 0x55f77dc926c9 in __rust_maybe_catch_panic /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libpanic_unwind/lib.rs:80:7 - #5 0x55f77dc9197c in std::panicking::try::h413b21cdcd6cfd86 /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panicking.rs:267:12 - #6 0x55f77dc9197c in std::panic::catch_unwind::hc5cc8ef2fd73424d /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/panic.rs:396:8 - #7 0x55f77dc9197c in std::rt::lang_start_internal::h2039f418ab92218f /rustc/c27f7568bc74c418996892028a629eed5a7f5f00/src/libstd/rt.rs:47:24 - #8 0x55f77dc01c61 in std::rt::lang_start::ha905d28f6b61d691 (/tmp/a+0xac61) - #9 0x55f77dc0163a in main (/tmp/a+0xa63a) - #10 0x7f9b3cf5bbba in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26bba) - #11 0x55f77dc01289 in _start (/tmp/a+0xa289) - -Address 0x7ffcc15f43d0 is located in stack of thread T0 at offset 48 in frame - #0 0x55f77dc0135f in a::main::hab3bd2a745c2d0ac (/tmp/a+0xa35f) +``` + +```shell +$ export RUSTFLAGS=-Zsanitizer=address RUSTDOCFLAGS=-Zsanitizer=address +$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu +==37882==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe400e6250 at pc 0x5609a841fb20 bp 0x7ffe400e6210 sp 0x7ffe400e6208 +READ of size 4 at 0x7ffe400e6250 thread T0 + #0 0x5609a841fb1f in example::main::h628ffc6626ed85b2 /.../src/main.rs:3:23 + ... + +Address 0x7ffe400e6250 is located in stack of thread T0 at offset 48 in frame + #0 0x5609a841f8af in example::main::h628ffc6626ed85b2 /.../src/main.rs:1 This frame has 1 object(s): - [32, 48) 'xs' <== Memory access at offset 48 overflows this variable + [32, 48) 'xs' (line 2) <== Memory access at offset 48 overflows this variable HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork (longjmp and C++ exceptions *are* supported) -SUMMARY: AddressSanitizer: stack-buffer-overflow (/tmp/a+0xa5c4) in a::main::hab3bd2a745c2d0ac +SUMMARY: AddressSanitizer: stack-buffer-overflow /.../src/main.rs:3:23 in example::main::h628ffc6626ed85b2 Shadow bytes around the buggy address: - 0x1000182b6820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b6830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b6840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b6850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b6860: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -=>0x1000182b6870: 00 00 00 00 f1 f1 f1 f1 00 00[f3]f3 00 00 00 00 - 0x1000182b6880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b6890: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b68a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b68b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x1000182b68c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014bf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014c10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014c20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +=>0x100048014c40: 00 00 00 00 f1 f1 f1 f1 00 00[f3]f3 00 00 00 00 + 0x100048014c50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014c60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x100048014c70: f1 f1 f1 f1 00 00 f3 f3 00 00 00 00 00 00 00 00 + 0x100048014c80: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 + 0x100048014c90: 00 00 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 - Partially addressable: 01 02 03 04 05 06 07 + Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 @@ -90,13 +95,12 @@ Shadow byte legend (one shadow byte represents 8 application bytes): Left alloca redzone: ca Right alloca redzone: cb Shadow gap: cc -==10029==ABORTING +==37882==ABORTING ``` Use of a stack object after its scope has already ended: -```shell -$ cat b.rs +```rust static mut P: *mut usize = std::ptr::null_mut(); fn main() { @@ -108,42 +112,38 @@ fn main() { std::ptr::write_volatile(P, 123); } } -$ rustc -Zsanitizer=address b.rs -$./b +``` + +```shell +$ export RUSTFLAGS=-Zsanitizer=address RUSTDOCFLAGS=-Zsanitizer=address +$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu ================================================================= -==424427==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7fff67be6be0 at pc 0x5647a3ea4658 bp 0x7fff67be6b90 sp 0x7fff67be6b88 -WRITE of size 8 at 0x7fff67be6be0 thread T0 - #0 0x5647a3ea4657 in core::ptr::write_volatile::h4b04601757d0376d (/tmp/b+0xb8657) - #1 0x5647a3ea4432 in b::main::h5574a756e615c9cf (/tmp/b+0xb8432) - #2 0x5647a3ea480b in std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::hd57e7ee01866077e (/tmp/b+0xb880b) - #3 0x5647a3eab412 in std::panicking::try::do_call::he0421ca82dd11ba3 (.llvm.8083791802951296215) (/tmp/b+0xbf412) - #4 0x5647a3eacb26 in __rust_maybe_catch_panic (/tmp/b+0xc0b26) - #5 0x5647a3ea5b66 in std::rt::lang_start_internal::h19bc96b28f670a64 (/tmp/b+0xb9b66) - #6 0x5647a3ea4788 in std::rt::lang_start::h642d10b4b6965fb8 (/tmp/b+0xb8788) - #7 0x5647a3ea449a in main (/tmp/b+0xb849a) - #8 0x7fd1d18b3bba in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26bba) - #9 0x5647a3df7299 in _start (/tmp/b+0xb299) - -Address 0x7fff67be6be0 is located in stack of thread T0 at offset 32 in frame - #0 0x5647a3ea433f in b::main::h5574a756e615c9cf (/tmp/b+0xb833f) +==39249==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffc7ed3e1a0 at pc 0x55c98b262a8e bp 0x7ffc7ed3e050 sp 0x7ffc7ed3e048 +WRITE of size 8 at 0x7ffc7ed3e1a0 thread T0 + #0 0x55c98b262a8d in core::ptr::write_volatile::he21f1df5a82f329a /.../src/rust/src/libcore/ptr/mod.rs:1048:5 + #1 0x55c98b262cd2 in example::main::h628ffc6626ed85b2 /.../src/main.rs:9:9 + ... + +Address 0x7ffc7ed3e1a0 is located in stack of thread T0 at offset 32 in frame + #0 0x55c98b262bdf in example::main::h628ffc6626ed85b2 /.../src/main.rs:3 This frame has 1 object(s): - [32, 40) 'x' <== Memory access at offset 32 is inside this variable + [32, 40) 'x' (line 6) <== Memory access at offset 32 is inside this variable HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork (longjmp and C++ exceptions *are* supported) -SUMMARY: AddressSanitizer: stack-use-after-scope (/tmp/b+0xb8657) in core::ptr::write_volatile::h4b04601757d0376d +SUMMARY: AddressSanitizer: stack-use-after-scope /.../src/rust/src/libcore/ptr/mod.rs:1048:5 in core::ptr::write_volatile::he21f1df5a82f329a Shadow bytes around the buggy address: - 0x10006cf74d20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -=>0x10006cf74d70: 00 00 00 00 00 00 00 00 f1 f1 f1 f1[f8]f3 f3 f3 - 0x10006cf74d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10006cf74dc0: f1 f1 f1 f1 00 f3 f3 f3 00 00 00 00 00 00 00 00 + 0x10000fd9fbe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10000fd9fbf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10000fd9fc00: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 + 0x10000fd9fc10: f8 f8 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10000fd9fc20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +=>0x10000fd9fc30: f1 f1 f1 f1[f8]f3 f3 f3 00 00 00 00 00 00 00 00 + 0x10000fd9fc40: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 + 0x10000fd9fc50: 00 00 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10000fd9fc60: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 f3 f3 + 0x10000fd9fc70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x10000fd9fc80: 00 00 00 00 f1 f1 f1 f1 00 00 f3 f3 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 @@ -164,17 +164,26 @@ Shadow byte legend (one shadow byte represents 8 application bytes): Left alloca redzone: ca Right alloca redzone: cb Shadow gap: cc -==424427==ABORTING +==39249==ABORTING ``` -## MemorySanitizer +# MemorySanitizer + +MemorySanitizer is detector of uninitialized reads. It is only supported on the +`x86_64-unknown-linux-gnu` target. + +MemorySanitizer requires all program code to be instrumented. C/C++ dependencies +need to be recompiled using Clang with `-fsanitize=memory` option. Failing to +achieve that will result in false positive reports. + +## Example -Use of uninitialized memory. Note that we are using `-Zbuild-std` to instrument -the standard library, and passing `-Zsanitizer-track-origins` to track the +Detecting the use of uninitialized memory. The `-Zbuild-std` flag rebuilds and +instruments the standard library, and is strictly necessary for the correct +operation of the tool. The `-Zsanitizer-track-origins` enables tracking of the origins of uninitialized memory: -```shell -$ cat src/main.rs +```rust use std::mem::MaybeUninit; fn main() { @@ -184,7 +193,9 @@ fn main() { println!("{}", a[2]); } } +``` +```shell $ export \ CC=clang \ CXX=clang++ \ @@ -193,7 +204,7 @@ $ export \ RUSTFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins' \ RUSTDOCFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins' $ cargo clean -$ cargo -Zbuild-std run --target x86_64-unknown-linux-gnu +$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu ==9416==WARNING: MemorySanitizer: use-of-uninitialized-value #0 0x560c04f7488a in core::fmt::num::imp::fmt_u64::haa293b0b098501ca $RUST/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/src/rust/src/libcore/fmt/num.rs:202:16 ... @@ -205,6 +216,55 @@ $ cargo -Zbuild-std run --target x86_64-unknown-linux-gnu #0 0x560c04b2bc50 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:3 ``` +# ThreadSanitizer + +ThreadSanitizer is a data race detection tool. It is supported on the following +targets: + +* `x86_64-apple-darwin` +* `x86_64-unknown-linux-gnu` + +To work correctly ThreadSanitizer needs to be "aware" of all synchronization +operations in a program. It generally achieves that through combination of +library interception (for example synchronization performed through +`pthread_mutex_lock` / `pthread_mutex_unlock`) and compile time instrumentation +(e.g. atomic operations). Using it without instrumenting all the program code +can lead to false positive reports. + +ThreadSanitizer does not support atomic fences `std::sync::atomic::fence`, +nor synchronization performed using inline assembly code. + +## Example + +```rust +static mut A: usize = 0; + +fn main() { + let t = std::thread::spawn(|| { + unsafe { A += 1 }; + }); + unsafe { A += 1 }; + + t.join().unwrap(); +} +``` + +```shell +$ export RUSTFLAGS=-Zsanitizer=thread RUSTDOCFLAGS=-Zsanitizer=thread +$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu +================== +WARNING: ThreadSanitizer: data race (pid=10574) + Read of size 8 at 0x5632dfe3d030 by thread T1: + #0 example::main::_$u7b$$u7b$closure$u7d$$u7d$::h23f64b0b2f8c9484 ../src/main.rs:5:18 (example+0x86cec) + ... + + Previous write of size 8 at 0x5632dfe3d030 by main thread: + #0 example::main::h628ffc6626ed85b2 /.../src/main.rs:7:14 (example+0x868c8) + ... + #11 main (example+0x86a1a) + + Location is global 'example::A::h43ac149ddf992709' of size 8 at 0x5632dfe3d030 (example+0x000000bd9030) +``` # Instrumentation of external dependencies and std @@ -231,6 +291,10 @@ In more practical terms when using cargo always remember to pass `--target` flag, so that rustflags will not be applied to build scripts and procedural macros. +# Symbolizing the Reports + +Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PATH`. + # Additional Information * [Sanitizers project page](https://github.com/google/sanitizers/wiki/) diff --git a/src/liballoc/collections/vec_deque.rs b/src/liballoc/collections/vec_deque.rs index 69284fbf1b37d..94532521a9066 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/src/liballoc/collections/vec_deque.rs @@ -959,6 +959,9 @@ impl VecDeque { /// Returns a pair of slices which contain, in order, the contents of the /// `VecDeque`. /// + /// If [`make_contiguous`](#method.make_contiguous) was previously called, all elements + /// of the `VecDeque` will be in the first slice and the second slice will be empty. + /// /// # Examples /// /// ``` @@ -989,6 +992,9 @@ impl VecDeque { /// Returns a pair of slices which contain, in order, the contents of the /// `VecDeque`. /// + /// If [`make_contiguous`](#method.make_contiguous) was previously called, all elements + /// of the `VecDeque` will be in the first slice and the second slice will be empty. + /// /// # Examples /// /// ``` @@ -2044,6 +2050,148 @@ impl VecDeque { } } + /// Rearranges the internal storage of this deque so it is one contiguous slice, which is then returned. + /// + /// This method does not allocate and does not change the order of the inserted elements. + /// As it returns a mutable slice, this can be used to sort or binary search a deque. + /// + /// Once the internal storage is contiguous, the [`as_slices`](#method.as_slices) and + /// [`as_mut_slices`](#method.as_mut_slices) methods will return the entire contents of the + /// `VecDeque` in a single slice. + /// + /// # Examples + /// + /// Sorting the content of a deque. + /// + /// ``` + /// #![feature(deque_make_contiguous)] + /// + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::with_capacity(15); + /// + /// buf.push_back(2); + /// buf.push_back(1); + /// buf.push_front(3); + /// + /// // sorting the deque + /// buf.make_contiguous().sort(); + /// assert_eq!(buf.as_slices(), (&[1, 2, 3] as &[_], &[] as &[_])); + /// + /// // sorting it in reverse order + /// buf.make_contiguous().sort_by(|a, b| b.cmp(a)); + /// assert_eq!(buf.as_slices(), (&[3, 2, 1] as &[_], &[] as &[_])); + /// ``` + /// + /// Getting immutable access to the contiguous slice. + /// + /// ```rust + /// #![feature(deque_make_contiguous)] + /// + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// + /// buf.push_back(2); + /// buf.push_back(1); + /// buf.push_front(3); + /// + /// buf.make_contiguous(); + /// if let (slice, &[]) = buf.as_slices() { + /// // we can now be sure that `slice` contains all elements of the deque, + /// // while still having immutable access to `buf`. + /// assert_eq!(buf.len(), slice.len()); + /// assert_eq!(slice, &[3, 2, 1] as &[_]); + /// } + /// ``` + #[unstable(feature = "deque_make_contiguous", issue = "none")] + pub fn make_contiguous(&mut self) -> &mut [T] { + if self.is_contiguous() { + let tail = self.tail; + let head = self.head; + return unsafe { &mut self.buffer_as_mut_slice()[tail..head] }; + } + + let buf = self.buf.ptr(); + let cap = self.cap(); + let len = self.len(); + + let free = self.tail - self.head; + let tail_len = cap - self.tail; + + if free >= tail_len { + // there is enough free space to copy the tail in one go, + // this means that we first shift the head backwards, and then + // copy the tail to the correct position. + // + // from: DEFGH....ABC + // to: ABCDEFGH.... + unsafe { + ptr::copy(buf, buf.add(tail_len), self.head); + // ...DEFGH.ABC + ptr::copy_nonoverlapping(buf.add(self.tail), buf, tail_len); + // ABCDEFGH.... + + self.tail = 0; + self.head = len; + } + } else if free >= self.head { + // there is enough free space to copy the head in one go, + // this means that we first shift the tail forwards, and then + // copy the head to the correct position. + // + // from: FGH....ABCDE + // to: ...ABCDEFGH. + unsafe { + ptr::copy(buf.add(self.tail), buf.add(self.head), tail_len); + // FGHABCDE.... + ptr::copy_nonoverlapping(buf, buf.add(self.head + tail_len), self.head); + // ...ABCDEFGH. + + self.tail = self.head; + self.head = self.tail + len; + } + } else { + // free is smaller than both head and tail, + // this means we have to slowly "swap" the tail and the head. + // + // from: EFGHI...ABCD or HIJK.ABCDEFG + // to: ABCDEFGHI... or ABCDEFGHIJK. + let mut left_edge: usize = 0; + let mut right_edge: usize = self.tail; + unsafe { + // The general problem looks like this + // GHIJKLM...ABCDEF - before any swaps + // ABCDEFM...GHIJKL - after 1 pass of swaps + // ABCDEFGHIJM...KL - swap until the left edge reaches the temp store + // - then restart the algorithm with a new (smaller) store + // Sometimes the temp store is reached when the right edge is at the end + // of the buffer - this means we've hit the right order with fewer swaps! + // E.g + // EF..ABCD + // ABCDEF.. - after four only swaps we've finished + while left_edge < len && right_edge != cap { + let mut right_offset = 0; + for i in left_edge..right_edge { + right_offset = (i - left_edge) % (cap - right_edge); + let src: isize = (right_edge + right_offset) as isize; + ptr::swap(buf.add(i), buf.offset(src)); + } + let n_ops = right_edge - left_edge; + left_edge += n_ops; + right_edge += right_offset + 1; + } + + self.tail = 0; + self.head = len; + } + } + + let tail = self.tail; + let head = self.head; + unsafe { &mut self.buffer_as_mut_slice()[tail..head] } + } + /// Rotates the double-ended queue `mid` places to the left. /// /// Equivalently, @@ -2803,63 +2951,16 @@ impl From> for Vec { /// assert_eq!(vec, [8, 9, 1, 2, 3, 4]); /// assert_eq!(vec.as_ptr(), ptr); /// ``` - fn from(other: VecDeque) -> Self { + fn from(mut other: VecDeque) -> Self { + other.make_contiguous(); + unsafe { let buf = other.buf.ptr(); let len = other.len(); - let tail = other.tail; - let head = other.head; let cap = other.cap(); - // Need to move the ring to the front of the buffer, as vec will expect this. - if other.is_contiguous() { - ptr::copy(buf.add(tail), buf, len); - } else { - if (tail - head) >= cmp::min(cap - tail, head) { - // There is enough free space in the centre for the shortest block so we can - // do this in at most three copy moves. - if (cap - tail) > head { - // right hand block is the long one; move that enough for the left - ptr::copy(buf.add(tail), buf.add(tail - head), cap - tail); - // copy left in the end - ptr::copy(buf, buf.add(cap - head), head); - // shift the new thing to the start - ptr::copy(buf.add(tail - head), buf, len); - } else { - // left hand block is the long one, we can do it in two! - ptr::copy(buf, buf.add(cap - tail), head); - ptr::copy(buf.add(tail), buf, cap - tail); - } - } else { - // Need to use N swaps to move the ring - // We can use the space at the end of the ring as a temp store - - let mut left_edge: usize = 0; - let mut right_edge: usize = tail; - - // The general problem looks like this - // GHIJKLM...ABCDEF - before any swaps - // ABCDEFM...GHIJKL - after 1 pass of swaps - // ABCDEFGHIJM...KL - swap until the left edge reaches the temp store - // - then restart the algorithm with a new (smaller) store - // Sometimes the temp store is reached when the right edge is at the end - // of the buffer - this means we've hit the right order with fewer swaps! - // E.g - // EF..ABCD - // ABCDEF.. - after four only swaps we've finished - - while left_edge < len && right_edge != cap { - let mut right_offset = 0; - for i in left_edge..right_edge { - right_offset = (i - left_edge) % (cap - right_edge); - let src: isize = (right_edge + right_offset) as isize; - ptr::swap(buf.add(i), buf.offset(src)); - } - let n_ops = right_edge - left_edge; - left_edge += n_ops; - right_edge += right_offset + 1; - } - } + if other.head != 0 { + ptr::copy(buf.add(other.tail), buf, len); } let out = Vec::from_raw_parts(buf, len, cap); mem::forget(other); diff --git a/src/liballoc/collections/vec_deque/tests.rs b/src/liballoc/collections/vec_deque/tests.rs index f2ce5b1d15dde..8ef5ec78e056e 100644 --- a/src/liballoc/collections/vec_deque/tests.rs +++ b/src/liballoc/collections/vec_deque/tests.rs @@ -1,6 +1,6 @@ use super::*; -use ::test; +use test; #[bench] #[cfg_attr(miri, ignore)] // Miri does not support benchmarks @@ -130,6 +130,87 @@ fn test_insert() { } } +#[test] +fn make_contiguous_big_tail() { + let mut tester = VecDeque::with_capacity(15); + + for i in 0..3 { + tester.push_back(i); + } + + for i in 3..10 { + tester.push_front(i); + } + + // 012......9876543 + assert_eq!(tester.capacity(), 15); + assert_eq!((&[9, 8, 7, 6, 5, 4, 3] as &[_], &[0, 1, 2] as &[_]), tester.as_slices()); + + let expected_start = tester.head; + tester.make_contiguous(); + assert_eq!(tester.tail, expected_start); + assert_eq!((&[9, 8, 7, 6, 5, 4, 3, 0, 1, 2] as &[_], &[] as &[_]), tester.as_slices()); +} + +#[test] +fn make_contiguous_big_head() { + let mut tester = VecDeque::with_capacity(15); + + for i in 0..8 { + tester.push_back(i); + } + + for i in 8..10 { + tester.push_front(i); + } + + // 01234567......98 + let expected_start = 0; + tester.make_contiguous(); + assert_eq!(tester.tail, expected_start); + assert_eq!((&[9, 8, 0, 1, 2, 3, 4, 5, 6, 7] as &[_], &[] as &[_]), tester.as_slices()); +} + +#[test] +fn make_contiguous_small_free() { + let mut tester = VecDeque::with_capacity(15); + + for i in 'A' as u8..'I' as u8 { + tester.push_back(i as char); + } + + for i in 'I' as u8..'N' as u8 { + tester.push_front(i as char); + } + + // ABCDEFGH...MLKJI + let expected_start = 0; + tester.make_contiguous(); + assert_eq!(tester.tail, expected_start); + assert_eq!( + (&['M', 'L', 'K', 'J', 'I', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] as &[_], &[] as &[_]), + tester.as_slices() + ); + + tester.clear(); + for i in 'I' as u8..'N' as u8 { + tester.push_back(i as char); + } + + for i in 'A' as u8..'I' as u8 { + tester.push_front(i as char); + } + + // IJKLM...HGFEDCBA + let expected_start = 0; + tester.make_contiguous(); + assert_eq!(tester.tail, expected_start); + assert_eq!( + (&['H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', 'I', 'J', 'K', 'L', 'M'] as &[_], &[] as &[_]), + tester.as_slices() + ); +} + #[test] fn test_remove() { // This test checks that every single combination of tail position, length, and diff --git a/src/librustc_error_codes/error_codes/E0466.md b/src/librustc_error_codes/error_codes/E0466.md index 443b7bae1345e..7aefedbc0875d 100644 --- a/src/librustc_error_codes/error_codes/E0466.md +++ b/src/librustc_error_codes/error_codes/E0466.md @@ -1,4 +1,4 @@ -Macro import declarations were malformed. +Macro import declaration was malformed. Erroneous code examples: diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 8e004e45b7a3a..f1ddf3c635f7a 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -274,19 +274,16 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { _memory_extra: &(), _alloc_id: AllocId, allocation: &Allocation, - static_def_id: Option, + _static_def_id: Option, is_write: bool, ) -> InterpResult<'tcx> { if is_write { throw_machine_stop_str!("can't write to global"); } - // If the static allocation is mutable or if it has relocations (it may be legal to mutate - // the memory behind that in the future), then we can't const prop it. + // If the static allocation is mutable, then we can't const prop it as its content + // might be different at runtime. if allocation.mutability == Mutability::Mut { - throw_machine_stop_str!("can't eval mutable globals in ConstProp"); - } - if static_def_id.is_some() && allocation.relocations().len() > 0 { - throw_machine_stop_str!("can't eval statics with pointers in ConstProp"); + throw_machine_stop_str!("can't access mutable globals in ConstProp"); } Ok(()) diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index d154de35043d6..f9e9a07914d69 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -13,6 +13,7 @@ use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; use rustc_target::spec::TargetTriple; +use std::collections::HashMap; use std::env; use std::io::{self, Write}; use std::panic; @@ -190,10 +191,23 @@ enum TestFailure { UnexpectedRunPass, } +enum DirState { + Temp(tempfile::TempDir), + Perm(PathBuf), +} + +impl DirState { + fn path(&self) -> &std::path::Path { + match self { + DirState::Temp(t) => t.path(), + DirState::Perm(p) => p.as_path(), + } + } +} + fn run_test( test: &str, cratename: &str, - filename: &FileName, line: usize, options: Options, should_panic: bool, @@ -206,47 +220,11 @@ fn run_test( mut error_codes: Vec, opts: &TestOptions, edition: Edition, + outdir: DirState, + path: PathBuf, ) -> Result<(), TestFailure> { let (test, line_offset) = make_test(test, Some(cratename), as_test_harness, opts, edition); - // FIXME(#44940): if doctests ever support path remapping, then this filename - // needs to be the result of `SourceMap::span_to_unmapped_path`. - let path = match filename { - FileName::Real(path) => path.clone(), - _ => PathBuf::from(r"doctest.rs"), - }; - - enum DirState { - Temp(tempfile::TempDir), - Perm(PathBuf), - } - - impl DirState { - fn path(&self) -> &std::path::Path { - match self { - DirState::Temp(t) => t.path(), - DirState::Perm(p) => p.as_path(), - } - } - } - - let outdir = if let Some(mut path) = options.persist_doctests { - path.push(format!( - "{}_{}", - filename.to_string().rsplit('/').next().unwrap().replace(".", "_"), - line - )); - std::fs::create_dir_all(&path).expect("Couldn't create directory for doctest executables"); - - DirState::Perm(path) - } else { - DirState::Temp( - TempFileBuilder::new() - .prefix("rustdoctest") - .tempdir() - .expect("rustdoc needs a tempdir"), - ) - }; let output_file = outdir.path().join("rust_out"); let rustc_binary = options @@ -639,6 +617,7 @@ pub struct Collector { position: Span, source_map: Option>, filename: Option, + visited_tests: HashMap<(String, usize), usize>, } impl Collector { @@ -662,6 +641,7 @@ impl Collector { position: DUMMY_SP, source_map, filename, + visited_tests: HashMap::new(), } } @@ -705,6 +685,48 @@ impl Tester for Collector { let target = self.options.target.clone(); let target_str = target.to_string(); + // FIXME(#44940): if doctests ever support path remapping, then this filename + // needs to be the result of `SourceMap::span_to_unmapped_path`. + let path = match &filename { + FileName::Real(path) => path.clone(), + _ => PathBuf::from(r"doctest.rs"), + }; + + let outdir = if let Some(mut path) = options.persist_doctests.clone() { + // For example `module/file.rs` would become `module_file_rs` + let folder_name = filename + .to_string() + .chars() + .map(|c| if c == '/' || c == '.' { '_' } else { c }) + .collect::(); + + path.push(format!( + "{name}_{line}_{number}", + name = folder_name, + number = { + // Increases the current test number, if this file already + // exists or it creates a new entry with a test number of 0. + self.visited_tests + .entry((folder_name.clone(), line)) + .and_modify(|v| *v += 1) + .or_insert(0) + }, + line = line, + )); + + std::fs::create_dir_all(&path) + .expect("Couldn't create directory for doctest executables"); + + DirState::Perm(path) + } else { + DirState::Temp( + TempFileBuilder::new() + .prefix("rustdoctest") + .tempdir() + .expect("rustdoc needs a tempdir"), + ) + }; + debug!("creating test {}: {}", name, test); self.tests.push(testing::TestDescAndFn { desc: testing::TestDesc { @@ -723,7 +745,6 @@ impl Tester for Collector { let res = run_test( &test, &cratename, - &filename, line, options, config.should_panic, @@ -736,6 +757,8 @@ impl Tester for Collector { config.error_codes, &opts, edition, + outdir, + path, ); if let Err(err) = res { diff --git a/src/test/ui/intrinsics/intrinsic-alignment.rs b/src/test/ui/intrinsics/intrinsic-alignment.rs index 02e3139d29444..896651361be2a 100644 --- a/src/test/ui/intrinsics/intrinsic-alignment.rs +++ b/src/test/ui/intrinsics/intrinsic-alignment.rs @@ -56,16 +56,6 @@ mod m { #[cfg(target_os = "windows")] mod m { #[main] - #[cfg(target_arch = "x86")] - pub fn main() { - unsafe { - assert_eq!(::rusti::pref_align_of::(), 8); - assert_eq!(::rusti::min_align_of::(), 8); - } - } - - #[main] - #[cfg(target_arch = "x86_64")] pub fn main() { unsafe { assert_eq!(::rusti::pref_align_of::(), 8); diff --git a/src/test/ui/structs-enums/rec-align-u64.rs b/src/test/ui/structs-enums/rec-align-u64.rs index 680a690ba34e3..b06e204d31bf0 100644 --- a/src/test/ui/structs-enums/rec-align-u64.rs +++ b/src/test/ui/structs-enums/rec-align-u64.rs @@ -67,13 +67,6 @@ mod m { #[cfg(target_os = "windows")] mod m { - #[cfg(target_arch = "x86")] - pub mod m { - pub fn align() -> usize { 8 } - pub fn size() -> usize { 16 } - } - - #[cfg(target_arch = "x86_64")] pub mod m { pub fn align() -> usize { 8 } pub fn size() -> usize { 16 }