提交 a5b09d35 编写于 作者: B bors

Auto merge of #70625 - Dylan-DPC:rollup-o8n2hw8, r=Dylan-DPC

Rollup of 7 pull requests

Successful merges:

 - #69425 (add fn make_contiguous to VecDeque)
 - #69458 (improve folder name for persistent doc tests)
 - #70268 (Document ThreadSanitizer in unstable-book)
 - #70600 (Ensure there are versions of test code for aarch64 windows)
 - #70606 (Clean up E0466 explanation)
 - #70614 (remove unnecessary relocation check in const_prop)
 - #70623 (Fix broken link in README)

Failed merges:

r? @ghost
......@@ -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
......
......@@ -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 <null> (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/)
......
......@@ -959,6 +959,9 @@ pub fn iter_mut(&mut self) -> IterMut<'_, T> {
/// 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 @@ pub fn as_slices(&self) -> (&[T], &[T]) {
/// 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 @@ pub fn resize_with(&mut self, new_len: usize, generator: impl FnMut() -> T) {
}
}
/// 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<T> From<VecDeque<T>> for Vec<T> {
/// assert_eq!(vec, [8, 9, 1, 2, 3, 4]);
/// assert_eq!(vec.as_ptr(), ptr);
/// ```
fn from(other: VecDeque<T>) -> Self {
fn from(mut other: VecDeque<T>) -> 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);
......
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
......
Macro import declarations were malformed.
Macro import declaration was malformed.
Erroneous code examples:
......
......@@ -274,19 +274,16 @@ fn before_access_global(
_memory_extra: &(),
_alloc_id: AllocId,
allocation: &Allocation<Self::PointerTag, Self::AllocExtra>,
static_def_id: Option<DefId>,
_static_def_id: Option<DefId>,
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(())
......
......@@ -13,6 +13,7 @@
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<String>,
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<Lrc<SourceMap>>,
filename: Option<PathBuf>,
visited_tests: HashMap<(String, usize), usize>,
}
impl Collector {
......@@ -662,6 +641,7 @@ pub fn new(
position: DUMMY_SP,
source_map,
filename,
visited_tests: HashMap::new(),
}
}
......@@ -705,6 +685,48 @@ fn add_test(&mut self, test: String, config: LangString, line: usize) {
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::<String>();
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 @@ fn add_test(&mut self, test: String, config: LangString, line: usize) {
let res = run_test(
&test,
&cratename,
&filename,
line,
options,
config.should_panic,
......@@ -736,6 +757,8 @@ fn add_test(&mut self, test: String, config: LangString, line: usize) {
config.error_codes,
&opts,
edition,
outdir,
path,
);
if let Err(err) = res {
......
......@@ -56,16 +56,6 @@ pub fn main() {
#[cfg(target_os = "windows")]
mod m {
#[main]
#[cfg(target_arch = "x86")]
pub fn main() {
unsafe {
assert_eq!(::rusti::pref_align_of::<u64>(), 8);
assert_eq!(::rusti::min_align_of::<u64>(), 8);
}
}
#[main]
#[cfg(target_arch = "x86_64")]
pub fn main() {
unsafe {
assert_eq!(::rusti::pref_align_of::<u64>(), 8);
......
......@@ -67,13 +67,6 @@ pub fn size() -> usize { 16 }
#[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 }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册