diff --git a/.github/pull_requests.toml b/.github/pull_requests.toml index 12113728fc722..f7c83f9647284 100644 --- a/.github/pull_requests.toml +++ b/.github/pull_requests.toml @@ -9,5 +9,6 @@ members = [ "remi-delmas-3000", "qinheping", "tautschnig", - "jaisnan" + "jaisnan", + "Jaisu-1" ] diff --git a/.github/workflows/kani.yml b/.github/workflows/kani.yml index e2783264cf81a..da0928998976c 100644 --- a/.github/workflows/kani.yml +++ b/.github/workflows/kani.yml @@ -9,6 +9,7 @@ on: paths: - 'library/**' - '.github/workflows/kani.yml' + - 'scripts/check_kani.sh' defaults: run: @@ -30,32 +31,8 @@ jobs: - name: Checkout Library uses: actions/checkout@v4 with: - path: verify-rust-std + path: head submodules: true - # We currently build Kani from a branch that tracks a rustc version compatible with this library version. - - name: Checkout `Kani` - uses: actions/checkout@v4 - with: - repository: model-checking/kani - path: kani - ref: features/verify-rust-std - - - name: Setup Dependencies - working-directory: kani - run: | - ./scripts/setup/${{ matrix.base }}/install_deps.sh - - - name: Build `Kani` - working-directory: kani - run: | - cargo build-dev --release - echo "$(pwd)/scripts" >> $GITHUB_PATH - - - name: Run tests - working-directory: verify-rust-std - env: - RUST_BACKTRACE: 1 - run: | - kani verify-std -Z unstable-options ./library --target-dir ${{ runner.temp }} -Z function-contracts \ - -Z mem-predicates -Z ptr-to-ref-cast-checks + - name: Run Kani Script + run: bash ./head/scripts/check_kani.sh ${{github.workspace}}/head diff --git a/.github/workflows/pr_approval.yml b/.github/workflows/pr_approval.yml index fbac5035e2d3a..329cf5b43bae2 100644 --- a/.github/workflows/pr_approval.yml +++ b/.github/workflows/pr_approval.yml @@ -58,7 +58,7 @@ jobs: pull_number }); - const relevantPaths = ['library/', 'doc/src/challenges/']; + const relevantPaths = ['library/', 'doc/src/challenges/', '.github/workflows']; const isRelevantPR = files.data.some(file => relevantPaths.some(path => file.filename.startsWith(path)) ); @@ -73,7 +73,6 @@ jobs: const tomlContent = fs.readFileSync('.github/pull_requests.toml', 'utf8'); console.log('TOML content:', tomlContent); const tomlData = toml.parse(tomlContent); - console.log('Parsed TOML data:', JSON.stringify(tomlData, null, 2)); if (!tomlData.committee || !Array.isArray(tomlData.committee.members)) { throw new Error('committee.members is not an array in the TOML file'); @@ -99,7 +98,7 @@ jobs: .map(review => review.user.login) ); - const requiredApprovals = 2; + const requiredApprovals = 1; const currentCountfromCommittee = Array.from(approvers) .filter(approver => requiredApprovers.includes(approver)) .length; @@ -124,7 +123,7 @@ jobs: pull_number }); - // Create or update check run + // Update check run const checkRuns = await github.rest.checks.listForRef({ owner, repo, @@ -132,29 +131,15 @@ jobs: check_name: checkName }); - // Reuse the same workflow everytime there's a new review submitted - // instead of creating new workflows. Better efficiency and readability - // as the number of workflows is kept to a minimal number - if (checkRuns.data.total_count > 0) { - await github.rest.checks.update({ - owner, - repo, - check_run_id: checkRuns.data.check_runs[0].id, - status: 'completed', - conclusion, - output - }); - } else { - await github.rest.checks.create({ - owner, - repo, - name: checkName, - head_sha: pr.data.head.sha, - status: 'completed', - conclusion, - output - }); - } + await github.rest.checks.create({ + owner, + repo, + name: checkName, + head_sha: pr.data.head.sha, + status: 'completed', + conclusion, + output + }); if (conclusion === 'failure') { core.setFailed(`PR needs at least ${requiredApprovals} total approvals and 2 required approvals. Current approvals: ${approvers.size}, Required approvals: ${requiredApprovals}`); diff --git a/.github/workflows/rustc.yml b/.github/workflows/rustc.yml index 1903dd98e5b2c..112c1f58f7ce8 100644 --- a/.github/workflows/rustc.yml +++ b/.github/workflows/rustc.yml @@ -11,6 +11,7 @@ on: - 'library/**' - 'rust-toolchain.toml' - '.github/workflows/rustc.yml' + - 'scripts/check_rustc.sh' defaults: run: @@ -29,35 +30,5 @@ jobs: with: path: head - - name: Checkout `upstream/master` - uses: actions/checkout@v4 - with: - repository: rust-lang/rust - path: upstream - fetch-depth: 0 - submodules: true - - # Run rustc twice in case the toolchain needs to be installed. - # Retrieve the commit id from the `rustc --version`. Output looks like: - # `rustc 1.80.0-nightly (84b40fc90 2024-05-27)` - - name: Checkout matching commit - run: | - cd head - rustc --version - COMMIT_ID=$(rustc --version | sed -e "s/.*(\(.*\) .*/\1/") - cd ../upstream - git checkout ${COMMIT_ID} - - - name: Copy Library - run: | - rm -rf upstream/library - cp -r head/library upstream - - - name: Run tests - working-directory: upstream - env: - # Avoid error due to unexpected `cfg`. - RUSTFLAGS: "--check-cfg cfg(kani) --check-cfg cfg(feature,values(any()))" - run: | - ./configure --set=llvm.download-ci-llvm=true - ./x test --stage 0 library/std + - name: Run rustc script + run: bash ./head/scripts/check_rustc.sh ${{github.workspace}}/head diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 21713571824ce..d5885d4ec7bb7 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -16,4 +16,7 @@ - [Core Transmutation](./challenges/0001-core-transmutation.md) - [Memory safety of core intrinsics](./challenges/0002-intrinsics-memory.md) - [Pointer Arithmetic](./challenges/0003-pointer-arithmentic.md) + - [Memory safety of BTreeMap's `btree::node` module](./challenges/0004-btree-node.md) - [Inductive data type](./challenges/0005-linked-list.md) + - [Contracts for SmallSort](./challenges/0008-smallsort.md) + - [Memory safety of String](./challenges/0010-string.md) diff --git a/doc/src/challenges/0004-btree-node.md b/doc/src/challenges/0004-btree-node.md new file mode 100644 index 0000000000000..6f29ce23eba73 --- /dev/null +++ b/doc/src/challenges/0004-btree-node.md @@ -0,0 +1,68 @@ +# Challenge 4: Memory safety of BTreeMap's `btree::node` module + +- **Status:** Open +- **Tracking Issue:** [Link to issue](https://github.com/model-checking/verify-rust-std/issues/25) +- **Start date:** *2024-07-01* +- **End date:** *2024-12-10* + +------------------- + + +## Goal + +Verify the memory safety of the [`alloc::collections::btree::node` module](https://github.com/rust-lang/rust/blob/c290e9de32e8ba6a673ef125fde40eadd395d170/library/alloc/src/collections/btree/node.rs). +This is one of the main modules used for implementing the `BTreeMap` collection, and it includes a lot of unsafe code. + +### Success Criteria + +The memory safety of all public functions (especially safe ones) containing unsafe code must be verified, e.g.: + +1. `LeafNode::new` +1. `NodeRef::as_internal_mut` +1. `NodeRef::len` +1. `NodeRef::ascend` +1. `NodeRef::first_edge` +1. `NodeRef::last_edge` +1. `NodeRef::first_kv` +1. `NodeRef::last_kv` +1. `NodeRef::into_leaf` +1. `NodeRef::keys` +1. `NodeRef::as_leaf_mut` +1. `NodeRef::into_leaf_mut` +1. `NodeRef::as_leaf_dying` +1. `NodeRef::pop_internal_level` +1. `NodeRef::push` +1. `Handle::left_edge` +1. `Handle::right_edge` +1. `Handle::left_kv` +1. `Handle::right_kv` +1. `Handle::descend` +1. `Handle::into_kv` +1. `Handle::key_mut` +1. `Handle::into_val_mut` +1. `Handle::into_kv_mut` +1. `Handle::into_kv_valmut` +1. `Handle::kv_mut` + +Verification must be unbounded for functions that use recursion or contain loops, e.g. + +1. `NodeRef::new_internal` +1. `Handle::insert_recursing` +1. `BalancingContext::do_merge` +1. `BalancingContext::merge_tracking_child_edge` +1. `BalancingContext::steal_left` +1. `BalancingContext::steal_right` +1. `BalancingContext::bulk_steal_left` +1. `BalancingContext::bulk_steal_right` + +### List of UBs + +All proofs must automatically ensure the absence of the following [undefined behaviors](https://github.com/rust-lang/reference/blob/142b2ed77d33f37a9973772bd95e6144ed9dce43/src/behavior-considered-undefined.md): + +* Accessing (loading from or storing to) a place that is dangling or based on a misaligned pointer. +* Reading from uninitialized memory. +* Mutating immutable bytes. +* Producing an invalid value + +Note: All solutions to verification challenges need to satisfy the criteria established in the [challenge book](../general-rules.md) +in addition to the ones listed above. diff --git a/doc/src/challenges/0008-smallsort.md b/doc/src/challenges/0008-smallsort.md new file mode 100644 index 0000000000000..b0a4a8f743275 --- /dev/null +++ b/doc/src/challenges/0008-smallsort.md @@ -0,0 +1,54 @@ +# Challenge 8: Contracts for SmallSort + +- **Status:** Open +- **Tracking Issue:** [Link to issue](https://github.com/model-checking/verify-rust-std/issues/56) +- **Start date:** *2024-08-17* +- **End date:** *2024-12-10* + +------------------- + + +## Goal + +The implementations of the traits `StableSmallSortTypeImpl`, `UnstableSmallSortTypeImpl`, and `UnstableSmallSortFreezeTypeImpl` in the `smallsort` [module](https://github.com/rust-lang/rust/blob/master/library/core/src/slice/sort/shared/smallsort.rs) of the Rust standard library are the sorting +algorithms optimized for slices with small lengths. +In this challenge, the goal is to, first prove the memory safety of the public functions in the `smallsort` module, and, second, write contracts for them to +show that the sorting algorithms actually sort the slices. + +### Success Criteria + +Prove absence of undefined behavior of the following public functions. + +1. `::small_sort` +2. `::small_sort` +3. `::small_sort` +4. `slice::sort::shared::smallsort::swap_if_less` +5. `slice::sort::shared::smallsort::insertion_sort_shift_left` +6. `slice::sort::shared::smallsort::sort4_stable` +7. `slice::sort::shared::smallsort::has_efficient_in_place_swap` + +Write contracts for the following public functions that show that they actually sort the slices. + +1. `::small_sort` +2. `::small_sort` +3. `::small_sort` + +The memory safety and the contracts of the above listed functions must be verified +for all possible slices with arbitrary valid length. + +Note that most of the functions listed above call functions that contain loops. +Function contracts and loop contracts of those callee functions may be required. + +### List of UBs + +In addition to any properties called out as `SAFETY` comments in the source +code, +all proofs must automatically ensure the absence of the following [undefined behaviors](https://github.com/rust-lang/reference/blob/142b2ed77d33f37a9973772bd95e6144ed9dce43/src/behavior-considered-undefined.md): + +* Accessing (loading from or storing to) a place that is dangling or based on a misaligned pointer. +* Reading from uninitialized memory. +* Mutating immutable bytes. +* Producing an invalid value + +Note: All solutions to verification challenges need to satisfy the criteria established in the [challenge book](../general-rules.md) +in addition to the ones listed above. diff --git a/doc/src/challenges/0010-string.md b/doc/src/challenges/0010-string.md new file mode 100644 index 0000000000000..1e7020c52cf53 --- /dev/null +++ b/doc/src/challenges/0010-string.md @@ -0,0 +1,75 @@ +# Challenge X: Memory safety of String + +- **Status:** Open +- **Tracking Issue:** [Link to issue](https://github.com/model-checking/verify-rust-std/issues/61) +- **Start date:** *2024-08-19* +- **End date:** *2024-12-10* + +------------------- + +## Goal + +In this challenge, the goal is to verify the memory safety of `std::string::String`. +Even though the majority of `String` methods are safe, many of them are safe abstractions over unsafe code. + +For instance, the `insert` method is implemented as follows in v1.80.1: +```rust +pub fn insert(&mut self, idx: usize, ch: char) { + assert!(self.is_char_boundary(idx)); + let mut bits = [0; 4]; + let bits = ch.encode_utf8(&mut bits).as_bytes(); + + unsafe { + self.insert_bytes(idx, bits); + } +} +``` +where `insert_bytes` has the following implementation: +```rust +unsafe fn insert_bytes(&mut self, idx: usize, bytes: &[u8]) { + let len = self.len(); + let amt = bytes.len(); + self.vec.reserve(amt); + + unsafe { + ptr::copy(self.vec.as_ptr().add(idx), self.vec.as_mut_ptr().add(idx + amt), len - idx); + ptr::copy_nonoverlapping(bytes.as_ptr(), self.vec.as_mut_ptr().add(idx), amt); + self.vec.set_len(len + amt); + } +} +``` +The call to the unsafe `insert_bytes` method (which itself contains unsafe code) makes `insert` susceptible to undefined behavior. + +### Success Criteria + +Verify the memory safety of all public functions that are safe abstractions over unsafe code: + +1. `from_utf16le` (unbounded) +1. `from_utf16le_lossy`(unbounded) +1. `from_utf16be` (unbounded) +1. `from_utf16be_lossy` (unbounded) +1. `pop` +1. `remove` +1. `remove_matches` (unbounded) +1. `retain` (unbounded) +1. `insert` +1. `insert_str` (unbounded) +1. `split_off` (unbounded) +1. `drain` +1. `replace_range` (unbounded) +1. `into_boxed_str` +1. `leak` + +Ones marked as unbounded must be verified for any string/slice length. + +### List of UBs + +All proofs must automatically ensure the absence of the following [undefined behaviors](https://github.com/rust-lang/reference/blob/142b2ed77d33f37a9973772bd95e6144ed9dce43/src/behavior-considered-undefined.md): + +* Accessing (loading from or storing to) a place that is dangling or based on a misaligned pointer. +* Reading from uninitialized memory. +* Mutating immutable bytes. +* Producing an invalid value + +Note: All solutions to verification challenges need to satisfy the criteria established in the [challenge book](../general-rules.md) +in addition to the ones listed above. diff --git a/library/Cargo.lock b/library/Cargo.lock new file mode 100644 index 0000000000000..223b61456c267 --- /dev/null +++ b/library/Cargo.lock @@ -0,0 +1,489 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "compiler_builtins", + "gimli 0.29.0", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "alloc" +version = "0.0.0" +dependencies = [ + "compiler_builtins", + "core", + "rand", + "rand_xorshift", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "cc" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "compiler_builtins" +version = "0.1.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb58b199190fcfe0846f55a3b545cd6b07a34bdd5930a476ff856f3ebcc5558a" +dependencies = [ + "cc", + "rustc-std-workspace-core", +] + +[[package]] +name = "core" +version = "0.0.0" +dependencies = [ + "rand", + "rand_xorshift", +] + +[[package]] +name = "dlmalloc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3264b043b8e977326c1ee9e723da2c1f8d09a99df52cacf00b4dbce5ac54414d" +dependencies = [ + "cfg-if", + "compiler_builtins", + "libc", + "rustc-std-workspace-core", + "windows-sys", +] + +[[package]] +name = "fortanix-sgx-abi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57cafc2274c10fab234f176b25903ce17e690fca7597090d50880e047a0389c5" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "rustc-std-workspace-core", + "rustc-std-workspace-std", + "unicode-width", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "allocator-api2", + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +dependencies = [ + "rustc-std-workspace-core", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "object" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +dependencies = [ + "compiler_builtins", + "memchr", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "panic_abort" +version = "0.0.0" +dependencies = [ + "alloc", + "cfg-if", + "compiler_builtins", + "core", + "libc", +] + +[[package]] +name = "panic_unwind" +version = "0.0.0" +dependencies = [ + "alloc", + "cfg-if", + "compiler_builtins", + "core", + "libc", + "unwind", +] + +[[package]] +name = "proc_macro" +version = "0.0.0" +dependencies = [ + "core", + "std", +] + +[[package]] +name = "profiler_builtins" +version = "0.0.0" +dependencies = [ + "cc", + "compiler_builtins", + "core", +] + +[[package]] +name = "r-efi" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e935efc5854715dfc0a4c9ef18dc69dee0ec3bf9cc3ab740db831c0fdd86a3" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "r-efi-alloc" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7" +dependencies = [ + "compiler_builtins", + "r-efi", + "rustc-std-workspace-core", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "rustc-std-workspace-alloc" +version = "1.99.0" +dependencies = [ + "alloc", +] + +[[package]] +name = "rustc-std-workspace-core" +version = "1.99.0" +dependencies = [ + "core", +] + +[[package]] +name = "rustc-std-workspace-std" +version = "1.99.0" +dependencies = [ + "std", +] + +[[package]] +name = "std" +version = "0.0.0" +dependencies = [ + "addr2line", + "alloc", + "cfg-if", + "compiler_builtins", + "core", + "dlmalloc", + "fortanix-sgx-abi", + "hashbrown", + "hermit-abi", + "libc", + "miniz_oxide", + "object", + "panic_abort", + "panic_unwind", + "profiler_builtins", + "r-efi", + "r-efi-alloc", + "rand", + "rand_xorshift", + "rustc-demangle", + "std_detect", + "unwind", + "wasi", +] + +[[package]] +name = "std_detect" +version = "0.1.5" +dependencies = [ + "cfg-if", + "compiler_builtins", + "libc", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "sysroot" +version = "0.0.0" +dependencies = [ + "proc_macro", + "std", + "test", +] + +[[package]] +name = "test" +version = "0.0.0" +dependencies = [ + "core", + "getopts", + "libc", + "std", +] + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", + "rustc-std-workspace-std", +] + +[[package]] +name = "unwind" +version = "0.0.0" +dependencies = [ + "cfg-if", + "compiler_builtins", + "core", + "libc", + "unwinding", +] + +[[package]] +name = "unwinding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a19a21a537f635c16c7576f22d0f2f7d63353c1337ad4ce0d8001c7952a25b" +dependencies = [ + "compiler_builtins", + "gimli 0.28.1", + "rustc-std-workspace-core", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/library/Cargo.toml b/library/Cargo.toml new file mode 100644 index 0000000000000..c4513b4c127d8 --- /dev/null +++ b/library/Cargo.toml @@ -0,0 +1,44 @@ +[workspace] +resolver = "1" +members = [ + "std", + "sysroot", +] + +exclude = [ + # stdarch has its own Cargo workspace + "stdarch", +] + +[profile.release.package.compiler_builtins] +# For compiler-builtins we always use a high number of codegen units. +# The goal here is to place every single intrinsic into its own object +# file to avoid symbol clashes with the system libgcc if possible. Note +# that this number doesn't actually produce this many object files, we +# just don't create more than this number of object files. +# +# It's a bit of a bummer that we have to pass this here, unfortunately. +# Ideally this would be specified through an env var to Cargo so Cargo +# knows how many CGUs are for this specific crate, but for now +# per-crate configuration isn't specifiable in the environment. +codegen-units = 10000 + +# These dependencies of the standard library implement symbolication for +# backtraces on most platforms. Their debuginfo causes both linking to be slower +# (more data to chew through) and binaries to be larger without really all that +# much benefit. This section turns them all to down to have no debuginfo which +# helps to improve link times a little bit. +[profile.release.package] +addr2line.debug = 0 +adler.debug = 0 +gimli.debug = 0 +miniz_oxide.debug = 0 +object.debug = 0 +rustc-demangle.debug = 0 + +[patch.crates-io] +# See comments in `library/rustc-std-workspace-core/README.md` for what's going on +# here +rustc-std-workspace-core = { path = 'rustc-std-workspace-core' } +rustc-std-workspace-alloc = { path = 'rustc-std-workspace-alloc' } +rustc-std-workspace-std = { path = 'rustc-std-workspace-std' } diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 3960f71681264..479eb0a2ba743 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -10,7 +10,10 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "0.1.40", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.114", features = ['rustc-dep-of-std'] } + +[target.'cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")))'.dependencies] +compiler_builtins = { version = "0.1.114", features = ["no-f16-f128"] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } @@ -20,6 +23,10 @@ rand_xorshift = "0.3.0" name = "alloctests" path = "tests/lib.rs" +[[test]] +name = "vec_deque_alloc_error" +path = "tests/vec_deque_alloc_error.rs" + [[bench]] name = "allocbenches" path = "benches/lib.rs" @@ -34,8 +41,8 @@ harness = false compiler-builtins-mem = ['compiler_builtins/mem'] compiler-builtins-c = ["compiler_builtins/c"] compiler-builtins-no-asm = ["compiler_builtins/no-asm"] +compiler-builtins-no-f16-f128 = ["compiler_builtins/no-f16-f128"] compiler-builtins-mangled-names = ["compiler_builtins/mangled-names"] -compiler-builtins-weak-intrinsics = ["compiler_builtins/weak-intrinsics"] # Make panics and failed asserts immediately abort without formatting any message panic_immediate_abort = ["core/panic_immediate_abort"] # Choose algorithms that are optimized for binary size instead of runtime performance @@ -43,9 +50,6 @@ optimize_for_size = ["core/optimize_for_size"] [lints.rust.unexpected_cfgs] level = "warn" -# x.py uses beta cargo, so `check-cfg` entries do not yet take effect -# for rust-lang/rust. But for users of `-Zbuild-std` it does. -# The unused warning is waiting for rust-lang/cargo#13925 to reach beta. check-cfg = [ 'cfg(bootstrap)', 'cfg(no_global_oom_handling)', diff --git a/library/alloc/benches/btree/map.rs b/library/alloc/benches/btree/map.rs index 4fe07eb02139f..3bddef5045a00 100644 --- a/library/alloc/benches/btree/map.rs +++ b/library/alloc/benches/btree/map.rs @@ -1,7 +1,8 @@ use std::collections::BTreeMap; use std::ops::RangeBounds; -use rand::{seq::SliceRandom, Rng}; +use rand::seq::SliceRandom; +use rand::Rng; use test::{black_box, Bencher}; macro_rules! map_insert_rand_bench { diff --git a/library/alloc/benches/linked_list.rs b/library/alloc/benches/linked_list.rs index 29c5ad2bc6eb2..b9322b6d4c3ea 100644 --- a/library/alloc/benches/linked_list.rs +++ b/library/alloc/benches/linked_list.rs @@ -1,4 +1,5 @@ use std::collections::LinkedList; + use test::Bencher; #[bench] diff --git a/library/alloc/benches/string.rs b/library/alloc/benches/string.rs index 5c95160ba2d14..e0dbe80d28896 100644 --- a/library/alloc/benches/string.rs +++ b/library/alloc/benches/string.rs @@ -1,4 +1,5 @@ use std::iter::repeat; + use test::{black_box, Bencher}; #[bench] diff --git a/library/alloc/benches/vec.rs b/library/alloc/benches/vec.rs index 8ebfe313dc5d5..13d784d3fd40e 100644 --- a/library/alloc/benches/vec.rs +++ b/library/alloc/benches/vec.rs @@ -1,5 +1,6 @@ -use rand::RngCore; use std::iter::repeat; + +use rand::RngCore; use test::{black_box, Bencher}; #[bench] diff --git a/library/alloc/benches/vec_deque.rs b/library/alloc/benches/vec_deque.rs index 35939f489b45d..fb1e2685cc333 100644 --- a/library/alloc/benches/vec_deque.rs +++ b/library/alloc/benches/vec_deque.rs @@ -1,7 +1,6 @@ -use std::{ - collections::{vec_deque, VecDeque}, - mem, -}; +use std::collections::{vec_deque, VecDeque}; +use std::mem; + use test::{black_box, Bencher}; #[bench] diff --git a/library/alloc/benches/vec_deque_append.rs b/library/alloc/benches/vec_deque_append.rs index 30b6e600e5adc..7c805da973763 100644 --- a/library/alloc/benches/vec_deque_append.rs +++ b/library/alloc/benches/vec_deque_append.rs @@ -1,4 +1,5 @@ -use std::{collections::VecDeque, time::Instant}; +use std::collections::VecDeque; +use std::time::Instant; const VECDEQUE_LEN: i32 = 100000; const WARMUP_N: usize = 100; diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 6677534eafc6e..db2d752cfde1c 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -2,16 +2,14 @@ #![stable(feature = "alloc_module", since = "1.28.0")] +#[stable(feature = "alloc_module", since = "1.28.0")] +#[doc(inline)] +pub use core::alloc::*; #[cfg(not(test))] use core::hint; - #[cfg(not(test))] use core::ptr::{self, NonNull}; -#[stable(feature = "alloc_module", since = "1.28.0")] -#[doc(inline)] -pub use core::alloc::*; - #[cfg(test)] mod tests; @@ -57,7 +55,7 @@ pub struct Global; #[cfg(test)] pub use std::alloc::Global; -/// Allocate memory with the global allocator. +/// Allocates memory with the global allocator. /// /// This function forwards calls to the [`GlobalAlloc::alloc`] method /// of the allocator registered with the `#[global_allocator]` attribute @@ -101,7 +99,7 @@ pub unsafe fn alloc(layout: Layout) -> *mut u8 { } } -/// Deallocate memory with the global allocator. +/// Deallocates memory with the global allocator. /// /// This function forwards calls to the [`GlobalAlloc::dealloc`] method /// of the allocator registered with the `#[global_allocator]` attribute @@ -119,7 +117,7 @@ pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) } } -/// Reallocate memory with the global allocator. +/// Reallocates memory with the global allocator. /// /// This function forwards calls to the [`GlobalAlloc::realloc`] method /// of the allocator registered with the `#[global_allocator]` attribute @@ -138,7 +136,7 @@ pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 unsafe { __rust_realloc(ptr, layout.size(), layout.align(), new_size) } } -/// Allocate zero-initialized memory with the global allocator. +/// Allocates zero-initialized memory with the global allocator. /// /// This function forwards calls to the [`GlobalAlloc::alloc_zeroed`] method /// of the allocator registered with the `#[global_allocator]` attribute @@ -345,7 +343,7 @@ extern "Rust" { fn __rust_alloc_error_handler(size: usize, align: usize) -> !; } -/// Signal a memory allocation error. +/// Signals a memory allocation error. /// /// Callers of memory allocation APIs wishing to cease execution /// in response to an allocation error are encouraged to call this function, @@ -424,29 +422,3 @@ pub mod __alloc_error_handler { } } } - -#[cfg(not(no_global_oom_handling))] -/// Specialize clones into pre-allocated, uninitialized memory. -/// Used by `Box::clone` and `Rc`/`Arc::make_mut`. -pub(crate) trait WriteCloneIntoRaw: Sized { - unsafe fn write_clone_into_raw(&self, target: *mut Self); -} - -#[cfg(not(no_global_oom_handling))] -impl WriteCloneIntoRaw for T { - #[inline] - default unsafe fn write_clone_into_raw(&self, target: *mut Self) { - // Having allocated *first* may allow the optimizer to create - // the cloned value in-place, skipping the local and move. - unsafe { target.write(self.clone()) }; - } -} - -#[cfg(not(no_global_oom_handling))] -impl WriteCloneIntoRaw for T { - #[inline] - unsafe fn write_clone_into_raw(&self, target: *mut Self) { - // We can always copy in-place, without ever involving a local value. - unsafe { target.copy_from_nonoverlapping(self, 1) }; - } -} diff --git a/library/alloc/src/alloc/tests.rs b/library/alloc/src/alloc/tests.rs index 1a5938fd34cf1..5d6077f057a2c 100644 --- a/library/alloc/src/alloc/tests.rs +++ b/library/alloc/src/alloc/tests.rs @@ -1,9 +1,10 @@ use super::*; extern crate test; -use crate::boxed::Box; use test::Bencher; +use crate::boxed::Box; + #[test] fn allocate_zeroed() { unsafe { diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs index 42f8a08a9e4ee..f86face3f90cb 100644 --- a/library/alloc/src/borrow.rs +++ b/library/alloc/src/borrow.rs @@ -2,21 +2,20 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::borrow::{Borrow, BorrowMut}; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; #[cfg(not(no_global_oom_handling))] use core::ops::{Add, AddAssign}; use core::ops::{Deref, DerefPure}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::borrow::{Borrow, BorrowMut}; +use Cow::*; use crate::fmt; #[cfg(not(no_global_oom_handling))] use crate::string::String; -use Cow::*; - #[stable(feature = "rust1", since = "1.0.0")] impl<'a, B: ?Sized> Borrow for Cow<'a, B> where diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 21d0050300170..38f50955b122e 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -145,8 +145,7 @@ //! to `into_iter()` for boxed slices will defer to the slice implementation on editions before //! 2024: //! -#![cfg_attr(bootstrap, doc = "```rust,edition2021,ignore")] -#![cfg_attr(not(bootstrap), doc = "```rust,edition2021")] +//! ```rust,edition2021 //! // Rust 2015, 2018, and 2021: //! //! # #![allow(boxed_slice_into_iter)] // override our `deny(warnings)` @@ -188,27 +187,29 @@ use core::any::Any; use core::async_iter::AsyncIterator; -use core::borrow; +#[cfg(not(no_global_oom_handling))] +use core::clone::CloneToUninit; use core::cmp::Ordering; use core::error::Error; -use core::fmt; use core::future::Future; use core::hash::{Hash, Hasher}; use core::iter::FusedIterator; -use core::marker::Tuple; -use core::marker::Unsize; +use core::marker::{Tuple, Unsize}; use core::mem::{self, SizedTypeProperties}; -use core::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; use core::ops::{ - CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, DerefPure, DispatchFromDyn, Receiver, + AsyncFn, AsyncFnMut, AsyncFnOnce, CoerceUnsized, Coroutine, CoroutineState, Deref, DerefMut, + DerefPure, DispatchFromDyn, Receiver, }; use core::pin::Pin; use core::ptr::{self, addr_of_mut, NonNull, Unique}; -use core::slice; use core::task::{Context, Poll}; +use core::{borrow, fmt, slice}; + +#[unstable(feature = "thin_box", issue = "92791")] +pub use thin::ThinBox; #[cfg(not(no_global_oom_handling))] -use crate::alloc::{handle_alloc_error, WriteCloneIntoRaw}; +use crate::alloc::handle_alloc_error; use crate::alloc::{AllocError, Allocator, Global, Layout}; #[cfg(not(no_global_oom_handling))] use crate::borrow::Cow; @@ -221,9 +222,6 @@ use crate::vec; #[cfg(not(no_global_oom_handling))] use crate::vec::Vec; -#[unstable(feature = "thin_box", issue = "92791")] -pub use thin::ThinBox; - mod thin; /// A pointer type that uniquely owns a heap allocation of type `T`. @@ -703,7 +701,7 @@ impl Box<[T]> { } /// Constructs a new boxed slice with uninitialized contents. Returns an error if - /// the allocation fails + /// the allocation fails. /// /// # Examples /// @@ -738,7 +736,7 @@ impl Box<[T]> { } /// Constructs a new boxed slice with uninitialized contents, with the memory - /// being filled with `0` bytes. Returns an error if the allocation fails + /// being filled with `0` bytes. Returns an error if the allocation fails. /// /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage /// of this method. @@ -830,6 +828,85 @@ impl Box<[T], A> { pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit], A> { unsafe { RawVec::with_capacity_zeroed_in(len, alloc).into_box(len) } } + + /// Constructs a new boxed slice with uninitialized contents in the provided allocator. Returns an error if + /// the allocation fails. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// use std::alloc::System; + /// + /// let mut values = Box::<[u32], _>::try_new_uninit_slice_in(3, System)?; + /// let values = unsafe { + /// // Deferred initialization: + /// values[0].as_mut_ptr().write(1); + /// values[1].as_mut_ptr().write(2); + /// values[2].as_mut_ptr().write(3); + /// values.assume_init() + /// }; + /// + /// assert_eq!(*values, [1, 2, 3]); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn try_new_uninit_slice_in( + len: usize, + alloc: A, + ) -> Result], A>, AllocError> { + let ptr = if T::IS_ZST || len == 0 { + NonNull::dangling() + } else { + let layout = match Layout::array::>(len) { + Ok(l) => l, + Err(_) => return Err(AllocError), + }; + alloc.allocate(layout)?.cast() + }; + unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, alloc).into_box(len)) } + } + + /// Constructs a new boxed slice with uninitialized contents in the provided allocator, with the memory + /// being filled with `0` bytes. Returns an error if the allocation fails. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// use std::alloc::System; + /// + /// let values = Box::<[u32], _>::try_new_zeroed_slice_in(3, System)?; + /// let values = unsafe { values.assume_init() }; + /// + /// assert_eq!(*values, [0, 0, 0]); + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn try_new_zeroed_slice_in( + len: usize, + alloc: A, + ) -> Result], A>, AllocError> { + let ptr = if T::IS_ZST || len == 0 { + NonNull::dangling() + } else { + let layout = match Layout::array::>(len) { + Ok(l) => l, + Err(_) => return Err(AllocError), + }; + alloc.allocate_zeroed(layout)?.cast() + }; + unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, alloc).into_box(len)) } + } } impl Box, A> { @@ -1096,6 +1173,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + #[must_use = "losing the pointer will leak memory"] #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub fn into_raw(b: Self) -> *mut T { @@ -1149,6 +1227,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn into_raw_with_allocator(b: Self) -> (*mut T, A) { @@ -1188,9 +1267,11 @@ impl Box { } /// Consumes and leaks the `Box`, returning a mutable reference, - /// `&'a mut T`. Note that the type `T` must outlive the chosen lifetime - /// `'a`. If the type has only static references, or none at all, then this - /// may be chosen to be `'static`. + /// `&'a mut T`. + /// + /// Note that the type `T` must outlive the chosen lifetime `'a`. If the type + /// has only static references, or none at all, then this may be chosen to be + /// `'static`. /// /// This function is mainly useful for data that lives for the remainder of /// the program's life. Dropping the returned reference will cause a memory @@ -1212,6 +1293,9 @@ impl Box { /// let static_ref: &'static mut usize = Box::leak(x); /// *static_ref += 1; /// assert_eq!(*static_ref, 42); + /// # // FIXME(https://github.com/rust-lang/miri/issues/3670): + /// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak. + /// # drop(unsafe { Box::from_raw(static_ref) }); /// ``` /// /// Unsized data: @@ -1221,6 +1305,9 @@ impl Box { /// let static_ref = Box::leak(x); /// static_ref[0] = 4; /// assert_eq!(*static_ref, [4, 2, 3]); + /// # // FIXME(https://github.com/rust-lang/miri/issues/3670): + /// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak. + /// # drop(unsafe { Box::from_raw(static_ref) }); /// ``` #[stable(feature = "box_leak", since = "1.26.0")] #[inline] @@ -1347,7 +1434,7 @@ impl Clone for Box { // Pre-allocate memory to allow writing the cloned value directly. let mut boxed = Self::new_uninit_in(self.1.clone()); unsafe { - (**self).write_clone_into_raw(boxed.as_mut_ptr()); + (**self).clone_to_uninit(boxed.as_mut_ptr()); boxed.assume_init() } } @@ -1767,7 +1854,7 @@ impl TryFrom> for Box<[T; N]> { } impl Box { - /// Attempt to downcast the box to a concrete type. + /// Attempts to downcast the box to a concrete type. /// /// # Examples /// @@ -1826,7 +1913,7 @@ impl Box { } impl Box { - /// Attempt to downcast the box to a concrete type. + /// Attempts to downcast the box to a concrete type. /// /// # Examples /// @@ -1885,7 +1972,7 @@ impl Box { } impl Box { - /// Attempt to downcast the box to a concrete type. + /// Attempts to downcast the box to a concrete type. /// /// # Examples /// @@ -2123,23 +2210,23 @@ impl FromIterator for Box<[I]> { /// This implementation is required to make sure that the `Box<[I]>: IntoIterator` /// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket. -#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] impl !Iterator for Box<[I], A> {} /// This implementation is required to make sure that the `&Box<[I]>: IntoIterator` /// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket. -#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] impl<'a, I, A: Allocator> !Iterator for &'a Box<[I], A> {} /// This implementation is required to make sure that the `&mut Box<[I]>: IntoIterator` /// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket. -#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] impl<'a, I, A: Allocator> !Iterator for &'a mut Box<[I], A> {} // Note: the `#[rustc_skip_during_method_dispatch(boxed_slice)]` on `trait IntoIterator` // hides this implementation from explicit `.into_iter()` calls on editions < 2024, // so those calls will still resolve to the slice implementation, by reference. -#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] impl IntoIterator for Box<[I], A> { type IntoIter = vec::IntoIter; type Item = I; @@ -2148,7 +2235,7 @@ impl IntoIterator for Box<[I], A> { } } -#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] impl<'a, I, A: Allocator> IntoIterator for &'a Box<[I], A> { type IntoIter = slice::Iter<'a, I>; type Item = &'a I; @@ -2157,7 +2244,7 @@ impl<'a, I, A: Allocator> IntoIterator for &'a Box<[I], A> { } } -#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] impl<'a, I, A: Allocator> IntoIterator for &'a mut Box<[I], A> { type IntoIter = slice::IterMut<'a, I>; type Item = &'a mut I; @@ -2167,7 +2254,7 @@ impl<'a, I, A: Allocator> IntoIterator for &'a mut Box<[I], A> { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl FromIterator for Box { fn from_iter>(iter: T) -> Self { String::from_iter(iter).into_boxed_str() @@ -2175,7 +2262,7 @@ impl FromIterator for Box { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl<'a> FromIterator<&'a char> for Box { fn from_iter>(iter: T) -> Self { String::from_iter(iter).into_boxed_str() @@ -2183,7 +2270,7 @@ impl<'a> FromIterator<&'a char> for Box { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl<'a> FromIterator<&'a str> for Box { fn from_iter>(iter: T) -> Self { String::from_iter(iter).into_boxed_str() @@ -2191,7 +2278,7 @@ impl<'a> FromIterator<&'a str> for Box { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl FromIterator for Box { fn from_iter>(iter: T) -> Self { String::from_iter(iter).into_boxed_str() @@ -2199,7 +2286,7 @@ impl FromIterator for Box { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl FromIterator> for Box { fn from_iter>>(iter: T) -> Self { String::from_iter(iter).into_boxed_str() @@ -2207,7 +2294,7 @@ impl FromIterator> for Box { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "boxed_str_from_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl<'a> FromIterator> for Box { fn from_iter>>(iter: T) -> Self { String::from_iter(iter).into_boxed_str() @@ -2373,7 +2460,7 @@ impl dyn Error + Send { let err: Box = self; ::downcast(err).map_err(|s| unsafe { // Reapply the `Send` marker. - Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send)) + mem::transmute::, Box>(s) }) } } @@ -2386,8 +2473,8 @@ impl dyn Error + Send + Sync { pub fn downcast(self: Box) -> Result, Box> { let err: Box = self; ::downcast(err).map_err(|s| unsafe { - // Reapply the `Send + Sync` marker. - Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send + Sync)) + // Reapply the `Send + Sync` markers. + mem::transmute::, Box>(s) }) } } diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 8b145b67bf186..9baded3a52141 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -1,7 +1,7 @@ -// Based on -// https://github.com/matthieu-m/rfc2580/blob/b58d1d3cba0d4b5e859d3617ea2d0943aaa31329/examples/thin.rs -// by matthieu-m -use crate::alloc::{self, Layout, LayoutError}; +//! Based on +//! +//! by matthieu-m + use core::error::Error; use core::fmt::{self, Debug, Display, Formatter}; #[cfg(not(no_global_oom_handling))] @@ -13,8 +13,9 @@ use core::mem; #[cfg(not(no_global_oom_handling))] use core::mem::SizedTypeProperties; use core::ops::{Deref, DerefMut}; -use core::ptr::Pointee; -use core::ptr::{self, NonNull}; +use core::ptr::{self, NonNull, Pointee}; + +use crate::alloc::{self, Layout, LayoutError}; /// ThinBox. /// diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 846b9a1404d27..cc5f33c368542 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -144,12 +144,11 @@ #![stable(feature = "rust1", since = "1.0.0")] use core::alloc::Allocator; -use core::fmt; use core::iter::{FusedIterator, InPlaceIterable, SourceIter, TrustedFused, TrustedLen}; use core::mem::{self, swap, ManuallyDrop}; use core::num::NonZero; use core::ops::{Deref, DerefMut}; -use core::ptr; +use core::{fmt, ptr}; use crate::alloc::Global; use crate::collections::TryReserveError; @@ -440,7 +439,7 @@ impl BinaryHeap { /// heap.push(4); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_binary_heap_constructor", issue = "112353")] + #[rustc_const_stable(feature = "const_binary_heap_constructor", since = "1.80.0")] #[must_use] pub const fn new() -> BinaryHeap { BinaryHeap { data: vec![] } @@ -484,7 +483,7 @@ impl BinaryHeap { /// heap.push(4); /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - #[rustc_const_unstable(feature = "const_binary_heap_constructor", issue = "112353")] + #[rustc_const_unstable(feature = "const_binary_heap_new_in", issue = "125961")] #[must_use] pub const fn new_in(alloc: A) -> BinaryHeap { BinaryHeap { data: Vec::new_in(alloc) } @@ -965,6 +964,7 @@ impl BinaryHeap { } /// Returns an iterator which retrieves elements in heap order. + /// /// This method consumes the original heap. /// /// # Examples @@ -1213,7 +1213,6 @@ impl BinaryHeap { /// Basic usage: /// /// ``` - /// #![feature(binary_heap_as_slice)] /// use std::collections::BinaryHeap; /// use std::io::{self, Write}; /// @@ -1222,7 +1221,7 @@ impl BinaryHeap { /// io::sink().write(heap.as_slice()).unwrap(); /// ``` #[must_use] - #[unstable(feature = "binary_heap_as_slice", issue = "83659")] + #[stable(feature = "binary_heap_as_slice", since = "1.80.0")] pub fn as_slice(&self) -> &[T] { self.data.as_slice() } @@ -1362,7 +1361,7 @@ struct Hole<'a, T: 'a> { } impl<'a, T> Hole<'a, T> { - /// Create a new `Hole` at index `pos`. + /// Creates a new `Hole` at index `pos`. /// /// Unsafe because pos must be within the data slice. #[inline] diff --git a/library/alloc/src/collections/binary_heap/tests.rs b/library/alloc/src/collections/binary_heap/tests.rs index d4bc6226a14a8..1cb07c6214953 100644 --- a/library/alloc/src/collections/binary_heap/tests.rs +++ b/library/alloc/src/collections/binary_heap/tests.rs @@ -1,7 +1,8 @@ +use std::panic::{catch_unwind, AssertUnwindSafe}; + use super::*; use crate::boxed::Box; use crate::testing::crash_test::{CrashTestDummy, Panic}; -use std::panic::{catch_unwind, AssertUnwindSafe}; #[test] fn test_iterator() { @@ -504,11 +505,12 @@ fn test_retain_catch_unwind() { #[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn panic_safe() { - use rand::seq::SliceRandom; use std::cmp; use std::panic::{self, AssertUnwindSafe}; use std::sync::atomic::{AtomicUsize, Ordering}; + use rand::seq::SliceRandom; + static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0); #[derive(Eq, PartialEq, Ord, Clone, Debug)] diff --git a/library/alloc/src/collections/btree/append.rs b/library/alloc/src/collections/btree/append.rs index b6989afb6255d..47372938fbeda 100644 --- a/library/alloc/src/collections/btree/append.rs +++ b/library/alloc/src/collections/btree/append.rs @@ -1,8 +1,9 @@ -use super::merge_iter::MergeIterInner; -use super::node::{self, Root}; use core::alloc::Allocator; use core::iter::FusedIterator; +use super::merge_iter::MergeIterInner; +use super::node::{self, Root}; + impl Root { /// Appends all key-value pairs from the union of two ascending iterators, /// incrementing a `length` variable along the way. The latter makes it diff --git a/library/alloc/src/collections/btree/fix.rs b/library/alloc/src/collections/btree/fix.rs index 91b61218005a6..4c1e19ead4031 100644 --- a/library/alloc/src/collections/btree/fix.rs +++ b/library/alloc/src/collections/btree/fix.rs @@ -1,7 +1,10 @@ -use super::map::MIN_LEN; -use super::node::{marker, ForceResult::*, Handle, LeftOrRight::*, NodeRef, Root}; use core::alloc::Allocator; +use super::map::MIN_LEN; +use super::node::ForceResult::*; +use super::node::LeftOrRight::*; +use super::node::{marker, Handle, NodeRef, Root}; + impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { /// Stocks up a possibly underfull node by merging with or stealing from a /// sibling. If successful but at the cost of shrinking the parent node, diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 3875f61efafdf..d84654e36d776 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1,4 +1,3 @@ -use crate::vec::Vec; use core::borrow::Borrow; use core::cmp::Ordering; use core::error::Error; @@ -10,20 +9,21 @@ use core::mem::{self, ManuallyDrop}; use core::ops::{Bound, Index, RangeBounds}; use core::ptr; -use crate::alloc::{Allocator, Global}; - use super::borrow::DormantMutRef; use super::dedup_sorted_iter::DedupSortedIter; use super::navigate::{LazyLeafRange, LeafRange}; -use super::node::{self, marker, ForceResult::*, Handle, NodeRef, Root}; -use super::search::{SearchBound, SearchResult::*}; +use super::node::ForceResult::*; +use super::node::{self, marker, Handle, NodeRef, Root}; +use super::search::SearchBound; +use super::search::SearchResult::*; use super::set_val::SetValZST; +use crate::alloc::{Allocator, Global}; +use crate::vec::Vec; mod entry; #[stable(feature = "rust1", since = "1.0.0")] pub use entry::{Entry, OccupiedEntry, OccupiedError, VacantEntry}; - use Entry::*; /// Minimum number of elements in a node that is not a root. @@ -2921,7 +2921,7 @@ impl<'a, K, V> Cursor<'a, K, V> { /// Returns a reference to the key and value of the next element without /// moving the cursor. /// - /// If the cursor is at the end of the map then `None` is returned + /// If the cursor is at the end of the map then `None` is returned. #[unstable(feature = "btree_cursors", issue = "107540")] pub fn peek_next(&self) -> Option<(&'a K, &'a V)> { self.clone().next() @@ -2963,7 +2963,7 @@ impl<'a, K, V, A> CursorMut<'a, K, V, A> { /// Returns a reference to the key and value of the next element without /// moving the cursor. /// - /// If the cursor is at the end of the map then `None` is returned + /// If the cursor is at the end of the map then `None` is returned. #[unstable(feature = "btree_cursors", issue = "107540")] pub fn peek_next(&mut self) -> Option<(&K, &mut V)> { let (k, v) = self.inner.peek_next()?; @@ -3061,7 +3061,7 @@ impl<'a, K, V, A> CursorMutKey<'a, K, V, A> { /// Returns a reference to the key and value of the next element without /// moving the cursor. /// - /// If the cursor is at the end of the map then `None` is returned + /// If the cursor is at the end of the map then `None` is returned. #[unstable(feature = "btree_cursors", issue = "107540")] pub fn peek_next(&mut self) -> Option<(&mut K, &mut V)> { let current = self.current.as_mut()?; diff --git a/library/alloc/src/collections/btree/map/entry.rs b/library/alloc/src/collections/btree/map/entry.rs index 66eb991c6d4b8..d128ad8ee5f6d 100644 --- a/library/alloc/src/collections/btree/map/entry.rs +++ b/library/alloc/src/collections/btree/map/entry.rs @@ -2,13 +2,12 @@ use core::fmt::{self, Debug}; use core::marker::PhantomData; use core::mem; -use crate::alloc::{Allocator, Global}; +use Entry::*; use super::super::borrow::DormantMutRef; use super::super::node::{marker, Handle, NodeRef}; use super::BTreeMap; - -use Entry::*; +use crate::alloc::{Allocator, Global}; /// A view into a single entry in a map, which may either be vacant or occupied. /// @@ -189,6 +188,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> Entry<'a, K, V, A> { } /// Ensures a value is in the entry by inserting, if empty, the result of the default function. + /// /// This method allows for generating key-derived values for insertion by providing the default /// function a reference to the key that was moved during the `.entry(key)` method call. /// diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 56620cf890db7..ff1254a5a0c42 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -1,3 +1,10 @@ +use core::assert_matches::assert_matches; +use std::iter; +use std::ops::Bound::{Excluded, Included, Unbounded}; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; + use super::*; use crate::boxed::Box; use crate::fmt::Debug; @@ -6,11 +13,6 @@ use crate::string::{String, ToString}; use crate::testing::crash_test::{CrashTestDummy, Panic}; use crate::testing::ord_chaos::{Cyclic3, Governed, Governor}; use crate::testing::rng::DeterministicRng; -use core::assert_matches::assert_matches; -use std::iter; -use std::ops::Bound::{Excluded, Included, Unbounded}; -use std::panic::{catch_unwind, AssertUnwindSafe}; -use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; // Minimum number of elements to insert, to guarantee a tree with 2 levels, // i.e., a tree who's root is an internal node at height 1, with edges to leaf nodes. @@ -1796,18 +1798,18 @@ fn test_ord_absence() { } fn map_debug(mut map: BTreeMap) { - format!("{map:?}"); - format!("{:?}", map.iter()); - format!("{:?}", map.iter_mut()); - format!("{:?}", map.keys()); - format!("{:?}", map.values()); - format!("{:?}", map.values_mut()); + let _ = format!("{map:?}"); + let _ = format!("{:?}", map.iter()); + let _ = format!("{:?}", map.iter_mut()); + let _ = format!("{:?}", map.keys()); + let _ = format!("{:?}", map.values()); + let _ = format!("{:?}", map.values_mut()); if true { - format!("{:?}", map.into_iter()); + let _ = format!("{:?}", map.into_iter()); } else if true { - format!("{:?}", map.into_keys()); + let _ = format!("{:?}", map.into_keys()); } else { - format!("{:?}", map.into_values()); + let _ = format!("{:?}", map.into_values()); } } diff --git a/library/alloc/src/collections/btree/mem.rs b/library/alloc/src/collections/btree/mem.rs index e1363d1ae1f6b..d738c5c47b4cc 100644 --- a/library/alloc/src/collections/btree/mem.rs +++ b/library/alloc/src/collections/btree/mem.rs @@ -1,6 +1,4 @@ -use core::intrinsics; -use core::mem; -use core::ptr; +use core::{intrinsics, mem, ptr}; /// This replaces the value behind the `v` unique reference by calling the /// relevant function. diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs index 5e6a26f65c41e..f5c621e2c1759 100644 --- a/library/alloc/src/collections/btree/navigate.rs +++ b/library/alloc/src/collections/btree/navigate.rs @@ -1,11 +1,10 @@ use core::borrow::Borrow; -use core::hint; use core::ops::RangeBounds; -use core::ptr; +use core::{hint, ptr}; -use super::node::{marker, ForceResult::*, Handle, NodeRef}; +use super::node::ForceResult::*; +use super::node::{marker, Handle, NodeRef}; use super::search::SearchBound; - use crate::alloc::Allocator; // `front` and `back` are always both `None` or both `Some`. pub struct LeafRange { diff --git a/library/alloc/src/collections/btree/remove.rs b/library/alloc/src/collections/btree/remove.rs index 0904299254f0a..c46422c2f1d45 100644 --- a/library/alloc/src/collections/btree/remove.rs +++ b/library/alloc/src/collections/btree/remove.rs @@ -1,7 +1,10 @@ -use super::map::MIN_LEN; -use super::node::{marker, ForceResult::*, Handle, LeftOrRight::*, NodeRef}; use core::alloc::Allocator; +use super::map::MIN_LEN; +use super::node::ForceResult::*; +use super::node::LeftOrRight::*; +use super::node::{marker, Handle, NodeRef}; + impl<'a, K: 'a, V: 'a> Handle, K, V, marker::LeafOrInternal>, marker::KV> { /// Removes a key-value pair from the tree, and returns that pair, as well as /// the leaf edge corresponding to that former pair. It's possible this empties diff --git a/library/alloc/src/collections/btree/search.rs b/library/alloc/src/collections/btree/search.rs index ad3522b4e0418..1d5c927175ea6 100644 --- a/library/alloc/src/collections/btree/search.rs +++ b/library/alloc/src/collections/btree/search.rs @@ -2,11 +2,12 @@ use core::borrow::Borrow; use core::cmp::Ordering; use core::ops::{Bound, RangeBounds}; -use super::node::{marker, ForceResult::*, Handle, NodeRef}; - use SearchBound::*; use SearchResult::*; +use super::node::ForceResult::*; +use super::node::{marker, Handle, NodeRef}; + pub enum SearchBound { /// An inclusive bound to look for, just like `Bound::Included(T)`. Included(T), diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index b0bd6ef2d3c63..973e7c660670c 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -1,4 +1,3 @@ -use crate::vec::Vec; use core::borrow::Borrow; use core::cmp::Ordering::{self, Equal, Greater, Less}; use core::cmp::{max, min}; @@ -6,14 +5,14 @@ use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; use core::iter::{FusedIterator, Peekable}; use core::mem::ManuallyDrop; -use core::ops::{BitAnd, BitOr, BitXor, RangeBounds, Sub}; +use core::ops::{BitAnd, BitOr, BitXor, Bound, RangeBounds, Sub}; use super::map::{BTreeMap, Keys}; use super::merge_iter::MergeIterInner; use super::set_val::SetValZST; use super::Recover; - use crate::alloc::{Allocator, Global}; +use crate::vec::Vec; /// An ordered set based on a B-Tree. /// @@ -1183,6 +1182,178 @@ impl BTreeSet { pub const fn is_empty(&self) -> bool { self.len() == 0 } + + /// Returns a [`Cursor`] pointing at the gap before the smallest element + /// greater than the given bound. + /// + /// Passing `Bound::Included(x)` will return a cursor pointing to the + /// gap before the smallest element greater than or equal to `x`. + /// + /// Passing `Bound::Excluded(x)` will return a cursor pointing to the + /// gap before the smallest element greater than `x`. + /// + /// Passing `Bound::Unbounded` will return a cursor pointing to the + /// gap before the smallest element in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_cursors)] + /// + /// use std::collections::BTreeSet; + /// use std::ops::Bound; + /// + /// let set = BTreeSet::from([1, 2, 3, 4]); + /// + /// let cursor = set.lower_bound(Bound::Included(&2)); + /// assert_eq!(cursor.peek_prev(), Some(&1)); + /// assert_eq!(cursor.peek_next(), Some(&2)); + /// + /// let cursor = set.lower_bound(Bound::Excluded(&2)); + /// assert_eq!(cursor.peek_prev(), Some(&2)); + /// assert_eq!(cursor.peek_next(), Some(&3)); + /// + /// let cursor = set.lower_bound(Bound::Unbounded); + /// assert_eq!(cursor.peek_prev(), None); + /// assert_eq!(cursor.peek_next(), Some(&1)); + /// ``` + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn lower_bound(&self, bound: Bound<&Q>) -> Cursor<'_, T> + where + T: Borrow + Ord, + Q: Ord, + { + Cursor { inner: self.map.lower_bound(bound) } + } + + /// Returns a [`CursorMut`] pointing at the gap before the smallest element + /// greater than the given bound. + /// + /// Passing `Bound::Included(x)` will return a cursor pointing to the + /// gap before the smallest element greater than or equal to `x`. + /// + /// Passing `Bound::Excluded(x)` will return a cursor pointing to the + /// gap before the smallest element greater than `x`. + /// + /// Passing `Bound::Unbounded` will return a cursor pointing to the + /// gap before the smallest element in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_cursors)] + /// + /// use std::collections::BTreeSet; + /// use std::ops::Bound; + /// + /// let mut set = BTreeSet::from([1, 2, 3, 4]); + /// + /// let mut cursor = set.lower_bound_mut(Bound::Included(&2)); + /// assert_eq!(cursor.peek_prev(), Some(&1)); + /// assert_eq!(cursor.peek_next(), Some(&2)); + /// + /// let mut cursor = set.lower_bound_mut(Bound::Excluded(&2)); + /// assert_eq!(cursor.peek_prev(), Some(&2)); + /// assert_eq!(cursor.peek_next(), Some(&3)); + /// + /// let mut cursor = set.lower_bound_mut(Bound::Unbounded); + /// assert_eq!(cursor.peek_prev(), None); + /// assert_eq!(cursor.peek_next(), Some(&1)); + /// ``` + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn lower_bound_mut(&mut self, bound: Bound<&Q>) -> CursorMut<'_, T, A> + where + T: Borrow + Ord, + Q: Ord, + { + CursorMut { inner: self.map.lower_bound_mut(bound) } + } + + /// Returns a [`Cursor`] pointing at the gap after the greatest element + /// smaller than the given bound. + /// + /// Passing `Bound::Included(x)` will return a cursor pointing to the + /// gap after the greatest element smaller than or equal to `x`. + /// + /// Passing `Bound::Excluded(x)` will return a cursor pointing to the + /// gap after the greatest element smaller than `x`. + /// + /// Passing `Bound::Unbounded` will return a cursor pointing to the + /// gap after the greatest element in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_cursors)] + /// + /// use std::collections::BTreeSet; + /// use std::ops::Bound; + /// + /// let set = BTreeSet::from([1, 2, 3, 4]); + /// + /// let cursor = set.upper_bound(Bound::Included(&3)); + /// assert_eq!(cursor.peek_prev(), Some(&3)); + /// assert_eq!(cursor.peek_next(), Some(&4)); + /// + /// let cursor = set.upper_bound(Bound::Excluded(&3)); + /// assert_eq!(cursor.peek_prev(), Some(&2)); + /// assert_eq!(cursor.peek_next(), Some(&3)); + /// + /// let cursor = set.upper_bound(Bound::Unbounded); + /// assert_eq!(cursor.peek_prev(), Some(&4)); + /// assert_eq!(cursor.peek_next(), None); + /// ``` + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn upper_bound(&self, bound: Bound<&Q>) -> Cursor<'_, T> + where + T: Borrow + Ord, + Q: Ord, + { + Cursor { inner: self.map.upper_bound(bound) } + } + + /// Returns a [`CursorMut`] pointing at the gap after the greatest element + /// smaller than the given bound. + /// + /// Passing `Bound::Included(x)` will return a cursor pointing to the + /// gap after the greatest element smaller than or equal to `x`. + /// + /// Passing `Bound::Excluded(x)` will return a cursor pointing to the + /// gap after the greatest element smaller than `x`. + /// + /// Passing `Bound::Unbounded` will return a cursor pointing to the + /// gap after the greatest element in the set. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_cursors)] + /// + /// use std::collections::BTreeSet; + /// use std::ops::Bound; + /// + /// let mut set = BTreeSet::from([1, 2, 3, 4]); + /// + /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Included(&3)) }; + /// assert_eq!(cursor.peek_prev(), Some(&3)); + /// assert_eq!(cursor.peek_next(), Some(&4)); + /// + /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Excluded(&3)) }; + /// assert_eq!(cursor.peek_prev(), Some(&2)); + /// assert_eq!(cursor.peek_next(), Some(&3)); + /// + /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Unbounded) }; + /// assert_eq!(cursor.peek_prev(), Some(&4)); + /// assert_eq!(cursor.peek_next(), None); + /// ``` + #[unstable(feature = "btree_cursors", issue = "107540")] + pub unsafe fn upper_bound_mut(&mut self, bound: Bound<&Q>) -> CursorMut<'_, T, A> + where + T: Borrow + Ord, + Q: Ord, + { + CursorMut { inner: self.map.upper_bound_mut(bound) } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1817,5 +1988,414 @@ impl<'a, T: Ord> Iterator for Union<'a, T> { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Union<'_, T> {} +/// A cursor over a `BTreeSet`. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth. +/// +/// Cursors always point to a gap between two elements in the set, and can +/// operate on the two immediately adjacent elements. +/// +/// A `Cursor` is created with the [`BTreeSet::lower_bound`] and [`BTreeSet::upper_bound`] methods. +#[derive(Clone)] +#[unstable(feature = "btree_cursors", issue = "107540")] +pub struct Cursor<'a, K: 'a> { + inner: super::map::Cursor<'a, K, SetValZST>, +} + +#[unstable(feature = "btree_cursors", issue = "107540")] +impl Debug for Cursor<'_, K> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Cursor") + } +} + +/// A cursor over a `BTreeSet` with editing operations. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth, and can +/// safely mutate the set during iteration. This is because the lifetime of its yielded +/// references is tied to its own lifetime, instead of just the underlying map. This means +/// cursors cannot yield multiple elements at once. +/// +/// Cursors always point to a gap between two elements in the set, and can +/// operate on the two immediately adjacent elements. +/// +/// A `CursorMut` is created with the [`BTreeSet::lower_bound_mut`] and [`BTreeSet::upper_bound_mut`] +/// methods. +#[unstable(feature = "btree_cursors", issue = "107540")] +pub struct CursorMut<'a, K: 'a, #[unstable(feature = "allocator_api", issue = "32838")] A = Global> +{ + inner: super::map::CursorMut<'a, K, SetValZST, A>, +} + +#[unstable(feature = "btree_cursors", issue = "107540")] +impl Debug for CursorMut<'_, K, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("CursorMut") + } +} + +/// A cursor over a `BTreeSet` with editing operations, and which allows +/// mutating elements. +/// +/// A `Cursor` is like an iterator, except that it can freely seek back-and-forth, and can +/// safely mutate the set during iteration. This is because the lifetime of its yielded +/// references is tied to its own lifetime, instead of just the underlying set. This means +/// cursors cannot yield multiple elements at once. +/// +/// Cursors always point to a gap between two elements in the set, and can +/// operate on the two immediately adjacent elements. +/// +/// A `CursorMutKey` is created from a [`CursorMut`] with the +/// [`CursorMut::with_mutable_key`] method. +/// +/// # Safety +/// +/// Since this cursor allows mutating elements, you must ensure that the +/// `BTreeSet` invariants are maintained. Specifically: +/// +/// * The newly inserted element must be unique in the tree. +/// * All elements in the tree must remain in sorted order. +#[unstable(feature = "btree_cursors", issue = "107540")] +pub struct CursorMutKey< + 'a, + K: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A = Global, +> { + inner: super::map::CursorMutKey<'a, K, SetValZST, A>, +} + +#[unstable(feature = "btree_cursors", issue = "107540")] +impl Debug for CursorMutKey<'_, K, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("CursorMutKey") + } +} + +impl<'a, K> Cursor<'a, K> { + /// Advances the cursor to the next gap, returning the element that it + /// moved over. + /// + /// If the cursor is already at the end of the set then `None` is returned + /// and the cursor is not moved. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn next(&mut self) -> Option<&'a K> { + self.inner.next().map(|(k, _)| k) + } + + /// Advances the cursor to the previous gap, returning the element that it + /// moved over. + /// + /// If the cursor is already at the start of the set then `None` is returned + /// and the cursor is not moved. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn prev(&mut self) -> Option<&'a K> { + self.inner.prev().map(|(k, _)| k) + } + + /// Returns a reference to next element without moving the cursor. + /// + /// If the cursor is at the end of the set then `None` is returned + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn peek_next(&self) -> Option<&'a K> { + self.inner.peek_next().map(|(k, _)| k) + } + + /// Returns a reference to the previous element without moving the cursor. + /// + /// If the cursor is at the start of the set then `None` is returned. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn peek_prev(&self) -> Option<&'a K> { + self.inner.peek_prev().map(|(k, _)| k) + } +} + +impl<'a, T, A> CursorMut<'a, T, A> { + /// Advances the cursor to the next gap, returning the element that it + /// moved over. + /// + /// If the cursor is already at the end of the set then `None` is returned + /// and the cursor is not moved. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn next(&mut self) -> Option<&T> { + self.inner.next().map(|(k, _)| k) + } + + /// Advances the cursor to the previous gap, returning the element that it + /// moved over. + /// + /// If the cursor is already at the start of the set then `None` is returned + /// and the cursor is not moved. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn prev(&mut self) -> Option<&T> { + self.inner.prev().map(|(k, _)| k) + } + + /// Returns a reference to the next element without moving the cursor. + /// + /// If the cursor is at the end of the set then `None` is returned. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn peek_next(&mut self) -> Option<&T> { + self.inner.peek_next().map(|(k, _)| k) + } + + /// Returns a reference to the previous element without moving the cursor. + /// + /// If the cursor is at the start of the set then `None` is returned. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn peek_prev(&mut self) -> Option<&T> { + self.inner.peek_prev().map(|(k, _)| k) + } + + /// Returns a read-only cursor pointing to the same location as the + /// `CursorMut`. + /// + /// The lifetime of the returned `Cursor` is bound to that of the + /// `CursorMut`, which means it cannot outlive the `CursorMut` and that the + /// `CursorMut` is frozen for the lifetime of the `Cursor`. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn as_cursor(&self) -> Cursor<'_, T> { + Cursor { inner: self.inner.as_cursor() } + } + + /// Converts the cursor into a [`CursorMutKey`], which allows mutating + /// elements in the tree. + /// + /// # Safety + /// + /// Since this cursor allows mutating elements, you must ensure that the + /// `BTreeSet` invariants are maintained. Specifically: + /// + /// * The newly inserted element must be unique in the tree. + /// * All elements in the tree must remain in sorted order. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub unsafe fn with_mutable_key(self) -> CursorMutKey<'a, T, A> { + CursorMutKey { inner: unsafe { self.inner.with_mutable_key() } } + } +} + +impl<'a, T, A> CursorMutKey<'a, T, A> { + /// Advances the cursor to the next gap, returning the element that it + /// moved over. + /// + /// If the cursor is already at the end of the set then `None` is returned + /// and the cursor is not moved. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn next(&mut self) -> Option<&mut T> { + self.inner.next().map(|(k, _)| k) + } + + /// Advances the cursor to the previous gap, returning the element that it + /// moved over. + /// + /// If the cursor is already at the start of the set then `None` is returned + /// and the cursor is not moved. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn prev(&mut self) -> Option<&mut T> { + self.inner.prev().map(|(k, _)| k) + } + + /// Returns a reference to the next element without moving the cursor. + /// + /// If the cursor is at the end of the set then `None` is returned + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn peek_next(&mut self) -> Option<&mut T> { + self.inner.peek_next().map(|(k, _)| k) + } + + /// Returns a reference to the previous element without moving the cursor. + /// + /// If the cursor is at the start of the set then `None` is returned. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn peek_prev(&mut self) -> Option<&mut T> { + self.inner.peek_prev().map(|(k, _)| k) + } + + /// Returns a read-only cursor pointing to the same location as the + /// `CursorMutKey`. + /// + /// The lifetime of the returned `Cursor` is bound to that of the + /// `CursorMutKey`, which means it cannot outlive the `CursorMutKey` and that the + /// `CursorMutKey` is frozen for the lifetime of the `Cursor`. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn as_cursor(&self) -> Cursor<'_, T> { + Cursor { inner: self.inner.as_cursor() } + } +} + +impl<'a, T: Ord, A: Allocator + Clone> CursorMut<'a, T, A> { + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap before the + /// newly inserted element. + /// + /// # Safety + /// + /// You must ensure that the `BTreeSet` invariants are maintained. + /// Specifically: + /// + /// * The newly inserted element must be unique in the tree. + /// * All elements in the tree must remain in sorted order. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub unsafe fn insert_after_unchecked(&mut self, value: T) { + unsafe { self.inner.insert_after_unchecked(value, SetValZST) } + } + + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap after the + /// newly inserted element. + /// + /// # Safety + /// + /// You must ensure that the `BTreeSet` invariants are maintained. + /// Specifically: + /// + /// * The newly inserted element must be unique in the tree. + /// * All elements in the tree must remain in sorted order. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub unsafe fn insert_before_unchecked(&mut self, value: T) { + unsafe { self.inner.insert_before_unchecked(value, SetValZST) } + } + + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap before the + /// newly inserted element. + /// + /// If the inserted element is not greater than the element before the + /// cursor (if any), or if it not less than the element after the cursor (if + /// any), then an [`UnorderedKeyError`] is returned since this would + /// invalidate the [`Ord`] invariant between the elements of the set. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn insert_after(&mut self, value: T) -> Result<(), UnorderedKeyError> { + self.inner.insert_after(value, SetValZST) + } + + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap after the + /// newly inserted element. + /// + /// If the inserted element is not greater than the element before the + /// cursor (if any), or if it not less than the element after the cursor (if + /// any), then an [`UnorderedKeyError`] is returned since this would + /// invalidate the [`Ord`] invariant between the elements of the set. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn insert_before(&mut self, value: T) -> Result<(), UnorderedKeyError> { + self.inner.insert_before(value, SetValZST) + } + + /// Removes the next element from the `BTreeSet`. + /// + /// The element that was removed is returned. The cursor position is + /// unchanged (before the removed element). + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn remove_next(&mut self) -> Option { + self.inner.remove_next().map(|(k, _)| k) + } + + /// Removes the precending element from the `BTreeSet`. + /// + /// The element that was removed is returned. The cursor position is + /// unchanged (after the removed element). + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn remove_prev(&mut self) -> Option { + self.inner.remove_prev().map(|(k, _)| k) + } +} + +impl<'a, T: Ord, A: Allocator + Clone> CursorMutKey<'a, T, A> { + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap before the + /// newly inserted element. + /// + /// # Safety + /// + /// You must ensure that the `BTreeSet` invariants are maintained. + /// Specifically: + /// + /// * The key of the newly inserted element must be unique in the tree. + /// * All elements in the tree must remain in sorted order. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub unsafe fn insert_after_unchecked(&mut self, value: T) { + unsafe { self.inner.insert_after_unchecked(value, SetValZST) } + } + + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap after the + /// newly inserted element. + /// + /// # Safety + /// + /// You must ensure that the `BTreeSet` invariants are maintained. + /// Specifically: + /// + /// * The newly inserted element must be unique in the tree. + /// * All elements in the tree must remain in sorted order. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub unsafe fn insert_before_unchecked(&mut self, value: T) { + unsafe { self.inner.insert_before_unchecked(value, SetValZST) } + } + + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap before the + /// newly inserted element. + /// + /// If the inserted element is not greater than the element before the + /// cursor (if any), or if it not less than the element after the cursor (if + /// any), then an [`UnorderedKeyError`] is returned since this would + /// invalidate the [`Ord`] invariant between the elements of the set. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn insert_after(&mut self, value: T) -> Result<(), UnorderedKeyError> { + self.inner.insert_after(value, SetValZST) + } + + /// Inserts a new element into the set in the gap that the + /// cursor is currently pointing to. + /// + /// After the insertion the cursor will be pointing at the gap after the + /// newly inserted element. + /// + /// If the inserted element is not greater than the element before the + /// cursor (if any), or if it not less than the element after the cursor (if + /// any), then an [`UnorderedKeyError`] is returned since this would + /// invalidate the [`Ord`] invariant between the elements of the set. + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn insert_before(&mut self, value: T) -> Result<(), UnorderedKeyError> { + self.inner.insert_before(value, SetValZST) + } + + /// Removes the next element from the `BTreeSet`. + /// + /// The element that was removed is returned. The cursor position is + /// unchanged (before the removed element). + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn remove_next(&mut self) -> Option { + self.inner.remove_next().map(|(k, _)| k) + } + + /// Removes the precending element from the `BTreeSet`. + /// + /// The element that was removed is returned. The cursor position is + /// unchanged (after the removed element). + #[unstable(feature = "btree_cursors", issue = "107540")] + pub fn remove_prev(&mut self) -> Option { + self.inner.remove_prev().map(|(k, _)| k) + } +} + +#[unstable(feature = "btree_cursors", issue = "107540")] +pub use super::map::UnorderedKeyError; + #[cfg(test)] mod tests; diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index 688ce57e9da6a..f947b6108c9a7 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -1,8 +1,9 @@ +use std::ops::Bound::{Excluded, Included}; +use std::panic::{catch_unwind, AssertUnwindSafe}; + use super::*; use crate::testing::crash_test::{CrashTestDummy, Panic}; use crate::testing::rng::DeterministicRng; -use std::ops::Bound::{Excluded, Included}; -use std::panic::{catch_unwind, AssertUnwindSafe}; #[test] fn test_clone_eq() { @@ -705,9 +706,9 @@ fn test_ord_absence() { } fn set_debug(set: BTreeSet) { - format!("{set:?}"); - format!("{:?}", set.iter()); - format!("{:?}", set.into_iter()); + let _ = format!("{set:?}"); + let _ = format!("{:?}", set.iter()); + let _ = format!("{:?}", set.into_iter()); } fn set_clone(mut set: BTreeSet) { diff --git a/library/alloc/src/collections/btree/split.rs b/library/alloc/src/collections/btree/split.rs index 638dc98fc3e41..c188ed1da6113 100644 --- a/library/alloc/src/collections/btree/split.rs +++ b/library/alloc/src/collections/btree/split.rs @@ -1,8 +1,10 @@ -use super::node::{ForceResult::*, Root}; -use super::search::SearchResult::*; use core::alloc::Allocator; use core::borrow::Borrow; +use super::node::ForceResult::*; +use super::node::Root; +use super::search::SearchResult::*; + impl Root { /// Calculates the length of both trees that result from splitting up /// a given number of distinct key-value pairs. diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 1c90c171a155b..0cd410c0fb7c1 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -13,12 +13,11 @@ #![stable(feature = "rust1", since = "1.0.0")] use core::cmp::Ordering; -use core::fmt; use core::hash::{Hash, Hasher}; use core::iter::FusedIterator; use core::marker::PhantomData; -use core::mem; use core::ptr::NonNull; +use core::{fmt, mem}; use super::SpecExtend; use crate::alloc::{Allocator, Global}; @@ -1495,6 +1494,14 @@ impl<'a, T, A: Allocator> Cursor<'a, T, A> { pub fn back(&self) -> Option<&'a T> { self.list.back() } + + /// Provides a reference to the cursor's parent list. + #[must_use] + #[inline(always)] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_list(&self) -> &'a LinkedList { + self.list + } } impl<'a, T, A: Allocator> CursorMut<'a, T, A> { @@ -1605,6 +1612,18 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { pub fn as_cursor(&self) -> Cursor<'_, T, A> { Cursor { list: self.list, current: self.current, index: self.index } } + + /// Provides a read-only reference to the cursor's parent list. + /// + /// The lifetime of the returned reference is bound to that of the + /// `CursorMut`, which means it cannot outlive the `CursorMut` and that the + /// `CursorMut` is frozen for the lifetime of the reference. + #[must_use] + #[inline(always)] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_list(&self) -> &LinkedList { + self.list + } } // Now the list editing operations @@ -1705,7 +1724,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { unsafe { self.current = unlinked_node.as_ref().next; self.list.unlink_node(unlinked_node); - let unlinked_node = Box::from_raw(unlinked_node.as_ptr()); + let unlinked_node = Box::from_raw_in(unlinked_node.as_ptr(), &self.list.alloc); Some(unlinked_node.element) } } @@ -1946,7 +1965,7 @@ where if (self.pred)(&mut node.as_mut().element) { // `unlink_node` is okay with aliasing `element` references. self.list.unlink_node(node); - return Some(Box::from_raw(node.as_ptr()).element); + return Some(Box::from_raw_in(node.as_ptr(), &self.list.alloc).element); } } } diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index 8dcd59d12d927..9b3c9ac5ce52e 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -1,12 +1,12 @@ -use super::*; -use crate::testing::crash_test::{CrashTestDummy, Panic}; -use crate::vec::Vec; - use std::panic::{catch_unwind, AssertUnwindSafe}; use std::thread; use rand::RngCore; +use super::*; +use crate::testing::crash_test::{CrashTestDummy, Panic}; +use crate::vec::Vec; + #[test] fn test_basic() { let mut m = LinkedList::>::new(); @@ -1164,3 +1164,40 @@ fn test_drop_panic() { assert_eq!(unsafe { DROPS }, 8); } + +#[test] +fn test_allocator() { + use core::alloc::{AllocError, Allocator, Layout}; + use core::cell::Cell; + + struct A { + has_allocated: Cell, + has_deallocated: Cell, + } + + unsafe impl Allocator for A { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + assert!(!self.has_allocated.get()); + self.has_allocated.set(true); + + Global.allocate(layout) + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + assert!(!self.has_deallocated.get()); + self.has_deallocated.set(true); + + unsafe { Global.deallocate(ptr, layout) } + } + } + + let alloc = &A { has_allocated: Cell::new(false), has_deallocated: Cell::new(false) }; + { + let mut list = LinkedList::new_in(alloc); + list.push_back(5u32); + list.remove(0); + } + + assert!(alloc.has_allocated.get()); + assert!(alloc.has_deallocated.get()); +} diff --git a/library/alloc/src/collections/mod.rs b/library/alloc/src/collections/mod.rs index 705b81535c279..020cf4d736510 100644 --- a/library/alloc/src/collections/mod.rs +++ b/library/alloc/src/collections/mod.rs @@ -27,33 +27,30 @@ pub mod btree_set { pub use super::btree::set::*; } +use core::fmt::Display; + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use binary_heap::BinaryHeap; - #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use btree_map::BTreeMap; - #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use btree_set::BTreeSet; - #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use linked_list::LinkedList; - #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use vec_deque::VecDeque; use crate::alloc::{Layout, LayoutError}; -use core::fmt::Display; /// The error type for `try_reserve` methods. #[derive(Clone, PartialEq, Eq, Debug)] diff --git a/library/alloc/src/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs index 1373e60149274..44fcef4ed7dc4 100644 --- a/library/alloc/src/collections/vec_deque/drain.rs +++ b/library/alloc/src/collections/vec_deque/drain.rs @@ -4,9 +4,8 @@ use core::mem::{self, SizedTypeProperties}; use core::ptr::NonNull; use core::{fmt, ptr}; -use crate::alloc::{Allocator, Global}; - use super::VecDeque; +use crate::alloc::{Allocator, Global}; /// A draining iterator over the elements of a `VecDeque`. /// diff --git a/library/alloc/src/collections/vec_deque/into_iter.rs b/library/alloc/src/collections/vec_deque/into_iter.rs index 692af7c197a30..2d283dac9a97a 100644 --- a/library/alloc/src/collections/vec_deque/into_iter.rs +++ b/library/alloc/src/collections/vec_deque/into_iter.rs @@ -1,10 +1,11 @@ use core::iter::{FusedIterator, TrustedLen}; +use core::mem::MaybeUninit; use core::num::NonZero; -use core::{array, fmt, mem::MaybeUninit, ops::Try, ptr}; - -use crate::alloc::{Allocator, Global}; +use core::ops::Try; +use core::{array, fmt, ptr}; use super::VecDeque; +use crate::alloc::{Allocator, Global}; /// An owning iterator over the elements of a `VecDeque`. /// @@ -132,7 +133,7 @@ impl Iterator for IntoIter { fn next_chunk( &mut self, ) -> Result<[Self::Item; N], array::IntoIter> { - let mut raw_arr = MaybeUninit::uninit_array(); + let mut raw_arr = [const { MaybeUninit::uninit() }; N]; let raw_arr_ptr = raw_arr.as_mut_ptr().cast(); let (head, tail) = self.inner.as_slices(); diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 4643a6bbe2ecd..dc725ec0f56e4 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -8,23 +8,19 @@ #![stable(feature = "rust1", since = "1.0.0")] use core::cmp::{self, Ordering}; -use core::fmt; use core::hash::{Hash, Hasher}; use core::iter::{repeat_n, repeat_with, ByRefSized}; -use core::mem::{ManuallyDrop, SizedTypeProperties}; -use core::ops::{Index, IndexMut, Range, RangeBounds}; -use core::ptr; -use core::slice; - // This is used in a bunch of intra-doc links. // FIXME: For some reason, `#[cfg(doc)]` wasn't sufficient, resulting in // failures in linkchecker even though rustdoc built the docs just fine. #[allow(unused_imports)] use core::mem; +use core::mem::{ManuallyDrop, SizedTypeProperties}; +use core::ops::{Index, IndexMut, Range, RangeBounds}; +use core::{fmt, ptr, slice}; use crate::alloc::{Allocator, Global}; -use crate::collections::TryReserveError; -use crate::collections::TryReserveErrorKind; +use crate::collections::{TryReserveError, TryReserveErrorKind}; use crate::raw_vec::RawVec; use crate::vec::Vec; @@ -164,6 +160,20 @@ impl VecDeque { self.buf.ptr() } + /// Appends an element to the buffer. + /// + /// # Safety + /// + /// May only be called if `deque.len() < deque.capacity()` + #[inline] + unsafe fn push_unchecked(&mut self, element: T) { + // SAFETY: Because of the precondition, it's guaranteed that there is space + // in the logical array after the last element. + unsafe { self.buffer_write(self.to_physical_idx(self.len), element) }; + // This can't overflow because `deque.len() < deque.capacity() <= usize::MAX`. + self.len += 1; + } + /// Moves an element out of the buffer #[inline] unsafe fn buffer_read(&mut self, off: usize) -> T { @@ -982,6 +992,8 @@ impl VecDeque { // `head` and `len` are at most `isize::MAX` and `target_cap < self.capacity()`, so nothing can // overflow. let tail_outside = (target_cap + 1..=self.capacity()).contains(&(self.head + self.len)); + // Used in the drop guard below. + let old_head = self.head; if self.len == 0 { self.head = 0; @@ -1034,12 +1046,74 @@ impl VecDeque { } self.head = new_head; } - self.buf.shrink_to_fit(target_cap); + + struct Guard<'a, T, A: Allocator> { + deque: &'a mut VecDeque, + old_head: usize, + target_cap: usize, + } + + impl Drop for Guard<'_, T, A> { + #[cold] + fn drop(&mut self) { + unsafe { + // SAFETY: This is only called if `buf.shrink_to_fit` unwinds, + // which is the only time it's safe to call `abort_shrink`. + self.deque.abort_shrink(self.old_head, self.target_cap) + } + } + } + + let guard = Guard { deque: self, old_head, target_cap }; + + guard.deque.buf.shrink_to_fit(target_cap); + + // Don't drop the guard if we didn't unwind. + mem::forget(guard); debug_assert!(self.head < self.capacity() || self.capacity() == 0); debug_assert!(self.len <= self.capacity()); } + /// Reverts the deque back into a consistent state in case `shrink_to` failed. + /// This is necessary to prevent UB if the backing allocator returns an error + /// from `shrink` and `handle_alloc_error` subsequently unwinds (see #123369). + /// + /// `old_head` refers to the head index before `shrink_to` was called. `target_cap` + /// is the capacity that it was trying to shrink to. + unsafe fn abort_shrink(&mut self, old_head: usize, target_cap: usize) { + // Moral equivalent of self.head + self.len <= target_cap. Won't overflow + // because `self.len <= target_cap`. + if self.head <= target_cap - self.len { + // The deque's buffer is contiguous, so no need to copy anything around. + return; + } + + // `shrink_to` already copied the head to fit into the new capacity, so this won't overflow. + let head_len = target_cap - self.head; + // `self.head > target_cap - self.len` => `self.len > target_cap - self.head =: head_len` so this must be positive. + let tail_len = self.len - head_len; + + if tail_len <= cmp::min(head_len, self.capacity() - target_cap) { + // There's enough spare capacity to copy the tail to the back (because `tail_len < self.capacity() - target_cap`), + // and copying the tail should be cheaper than copying the head (because `tail_len <= head_len`). + + unsafe { + // The old tail and the new tail can't overlap because the head slice lies between them. The + // head slice ends at `target_cap`, so that's where we copy to. + self.copy_nonoverlapping(0, target_cap, tail_len); + } + } else { + // Either there's not enough spare capacity to make the deque contiguous, or the head is shorter than the tail + // (and therefore hopefully cheaper to copy). + unsafe { + // The old and the new head slice can overlap, so we can't use `copy_nonoverlapping` here. + self.copy(self.head, old_head, head_len); + self.head = old_head; + } + } + } + /// Shortens the deque, keeping the first `len` elements and dropping /// the rest. /// @@ -2847,6 +2921,14 @@ impl Extend for VecDeque { fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } + + #[inline] + unsafe fn extend_one_unchecked(&mut self, item: T) { + // SAFETY: Our preconditions ensure the space has been reserved, and `extend_reserve` is implemented correctly. + unsafe { + self.push_unchecked(item); + } + } } #[stable(feature = "extend_ref", since = "1.2.0")] @@ -2864,6 +2946,14 @@ impl<'a, T: 'a + Copy, A: Allocator> Extend<&'a T> for VecDeque { fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } + + #[inline] + unsafe fn extend_one_unchecked(&mut self, &item: &'a T) { + // SAFETY: Our preconditions ensure the space has been reserved, and `extend_reserve` is implemented correctly. + unsafe { + self.push_unchecked(item); + } + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs index dccf40ccb38aa..a9b0fd073b548 100644 --- a/library/alloc/src/collections/vec_deque/spec_extend.rs +++ b/library/alloc/src/collections/vec_deque/spec_extend.rs @@ -1,9 +1,9 @@ -use crate::alloc::Allocator; -use crate::vec; use core::iter::TrustedLen; use core::slice; use super::VecDeque; +use crate::alloc::Allocator; +use crate::vec; // Specialization trait used for VecDeque::extend pub(super) trait SpecExtend { @@ -21,21 +21,12 @@ where // self.push_back(item); // } - // May only be called if `deque.len() < deque.capacity()` - unsafe fn push_unchecked(deque: &mut VecDeque, element: T) { - // SAFETY: Because of the precondition, it's guaranteed that there is space - // in the logical array after the last element. - unsafe { deque.buffer_write(deque.to_physical_idx(deque.len), element) }; - // This can't overflow because `deque.len() < deque.capacity() <= usize::MAX`. - deque.len += 1; - } - while let Some(element) = iter.next() { let (lower, _) = iter.size_hint(); self.reserve(lower.saturating_add(1)); // SAFETY: We just reserved space for at least one element. - unsafe { push_unchecked(self, element) }; + unsafe { self.push_unchecked(element) }; // Inner loop to avoid repeatedly calling `reserve`. while self.len < self.capacity() { @@ -43,7 +34,7 @@ where return; }; // SAFETY: The loop condition guarantees that `self.len() < self.capacity()`. - unsafe { push_unchecked(self, element) }; + unsafe { self.push_unchecked(element) }; } } } diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index b13af93d06c57..e32676a65432b 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -3,25 +3,21 @@ #[cfg(test)] mod tests; -use crate::borrow::{Cow, ToOwned}; -use crate::boxed::Box; -use crate::rc::Rc; -use crate::slice::hack::into_vec; -use crate::string::String; -use crate::vec::Vec; use core::borrow::Borrow; use core::ffi::{c_char, CStr}; -use core::fmt; -use core::mem; use core::num::NonZero; -use core::ops; -use core::ptr; -use core::slice; use core::slice::memchr; use core::str::{self, Utf8Error}; +use core::{fmt, mem, ops, ptr, slice}; +use crate::borrow::{Cow, ToOwned}; +use crate::boxed::Box; +use crate::rc::Rc; +use crate::slice::hack::into_vec; +use crate::string::String; #[cfg(target_has_atomic = "ptr")] use crate::sync::Arc; +use crate::vec::Vec; /// A type representing an owned, C-compatible, nul-terminated string with no nul bytes in the /// middle. @@ -911,7 +907,7 @@ impl From<&CStr> for Rc { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] impl Default for Rc { /// Creates an empty CStr inside an Rc /// diff --git a/library/alloc/src/ffi/c_str/tests.rs b/library/alloc/src/ffi/c_str/tests.rs index 9f51e17a427f5..8b7172b3f20a9 100644 --- a/library/alloc/src/ffi/c_str/tests.rs +++ b/library/alloc/src/ffi/c_str/tests.rs @@ -1,10 +1,10 @@ -use super::*; use core::assert_matches::assert_matches; use core::ffi::FromBytesUntilNulError; -use core::hash::{Hash, Hasher}; - #[allow(deprecated)] use core::hash::SipHasher13 as DefaultHasher; +use core::hash::{Hash, Hasher}; + +use super::*; #[test] fn c_to_rust() { diff --git a/library/alloc/src/ffi/mod.rs b/library/alloc/src/ffi/mod.rs index 9fc1acc231bff..4f9dc40a3cfc9 100644 --- a/library/alloc/src/ffi/mod.rs +++ b/library/alloc/src/ffi/mod.rs @@ -80,13 +80,12 @@ #![stable(feature = "alloc_ffi", since = "1.64.0")] -#[doc(no_inline)] -#[stable(feature = "alloc_c_string", since = "1.64.0")] -pub use self::c_str::{FromVecWithNulError, IntoStringError, NulError}; - #[doc(inline)] #[stable(feature = "alloc_c_string", since = "1.64.0")] pub use self::c_str::CString; +#[doc(no_inline)] +#[stable(feature = "alloc_c_string", since = "1.64.0")] +pub use self::c_str::{FromVecWithNulError, IntoStringError, NulError}; #[unstable(feature = "c_str_module", issue = "112134")] pub mod c_str; diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index ae44cab8131b5..4b9b90fc1f157 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -12,6 +12,7 @@ //! Some examples of the [`format!`] extension are: //! //! ``` +//! # #![allow(unused_must_use)] //! format!("Hello"); // => "Hello" //! format!("Hello, {}!", "world"); // => "Hello, world!" //! format!("The number is {}", 1); // => "The number is 1" @@ -50,6 +51,7 @@ //! the iterator advances. This leads to behavior like this: //! //! ``` +//! # #![allow(unused_must_use)] //! format!("{1} {} {0} {}", 1, 2); // => "2 1 1 2" //! ``` //! @@ -77,6 +79,7 @@ //! For example, the following [`format!`] expressions all use named arguments: //! //! ``` +//! # #![allow(unused_must_use)] //! format!("{argument}", argument = "test"); // => "test" //! format!("{name} {}", 1, name = 2); // => "2 1" //! format!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" @@ -86,6 +89,7 @@ //! reference a variable with that name in the current scope. //! //! ``` +//! # #![allow(unused_must_use)] //! let argument = 2 + 2; //! format!("{argument}"); // => "4" //! @@ -596,8 +600,7 @@ pub use core::fmt::{LowerHex, Pointer, UpperHex}; #[cfg(not(no_global_oom_handling))] use crate::string; -/// The `format` function takes an [`Arguments`] struct and returns the resulting -/// formatted string. +/// Takes an [`Arguments`] struct and returns the resulting formatted string. /// /// The [`Arguments`] instance can be created with the [`format_args!`] macro. /// diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 4ac0c9b15be7a..28b08ef561143 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -86,13 +86,13 @@ #![warn(multiple_supertrait_upcastable)] #![allow(internal_features)] #![allow(rustdoc::redundant_explicit_links)] +#![warn(rustdoc::unescaped_backticks)] #![deny(ffi_unwind_calls)] // // Library features: // tidy-alphabetical-start #![cfg_attr(not(no_global_oom_handling), feature(const_alloc_error))] #![cfg_attr(not(no_global_oom_handling), feature(const_btree_len))] -#![cfg_attr(test, feature(is_sorted))] #![cfg_attr(test, feature(new_uninit))] #![feature(alloc_layout_extra)] #![feature(allocator_api)] @@ -101,8 +101,10 @@ #![feature(array_windows)] #![feature(ascii_char)] #![feature(assert_matches)] +#![feature(async_closure)] #![feature(async_fn_traits)] #![feature(async_iterator)] +#![feature(clone_to_uninit)] #![feature(coerce_unsized)] #![feature(const_align_of_val)] #![feature(const_box)] @@ -115,19 +117,17 @@ #![feature(const_pin)] #![feature(const_refs_to_cell)] #![feature(const_size_of_val)] -#![feature(const_waker)] #![feature(core_intrinsics)] #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] #![feature(dispatch_from_dyn)] #![feature(error_generic_member_access)] -#![feature(error_in_core)] #![feature(exact_size_is_empty)] #![feature(extend_one)] +#![feature(extend_one_unchecked)] #![feature(fmt_internals)] #![feature(fn_traits)] #![feature(hasher_prefixfree_extras)] -#![feature(hint_assert_unchecked)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] @@ -135,7 +135,6 @@ #![feature(layout_for_ptr)] #![feature(local_waker)] #![feature(maybe_uninit_slice)] -#![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_uninit_array_transpose)] #![feature(panic_internals)] #![feature(pattern)] @@ -166,18 +165,15 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(exclusive_range_pattern))] #![cfg_attr(not(test), feature(coroutine_trait))] #![cfg_attr(test, feature(panic_update_hook))] #![cfg_attr(test, feature(test))] #![feature(allocator_internals)] #![feature(allow_internal_unstable)] -#![feature(c_unwind)] #![feature(cfg_sanitize)] #![feature(const_mut_refs)] #![feature(const_precise_live_drops)] #![feature(const_ptr_write)] -#![feature(const_trait_impl)] #![feature(const_try)] #![feature(decl_macro)] #![feature(dropck_eyepatch)] @@ -260,6 +256,7 @@ pub mod vec; #[unstable(feature = "liballoc_internals", issue = "none", reason = "implementation detail")] pub mod __export { pub use core::format_args; + pub use core::hint::must_use; } #[cfg(test)] diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 0f767df6063a3..8c6a367869ce0 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -41,18 +41,18 @@ #[allow_internal_unstable(rustc_attrs, liballoc_internals)] macro_rules! vec { () => ( - $crate::__rust_force_expr!($crate::vec::Vec::new()) + $crate::vec::Vec::new() ); ($elem:expr; $n:expr) => ( - $crate::__rust_force_expr!($crate::vec::from_elem($elem, $n)) + $crate::vec::from_elem($elem, $n) ); ($($x:expr),+ $(,)?) => ( - $crate::__rust_force_expr!(<[_]>::into_vec( + <[_]>::into_vec( // This rustc_box is not required, but it produces a dramatic improvement in compile // time when constructing arrays with many elements. #[rustc_box] $crate::boxed::Box::new([$($x),+]) - )) + ) ); } @@ -111,6 +111,7 @@ macro_rules! vec { /// # Examples /// /// ``` +/// # #![allow(unused_must_use)] /// format!("test"); // => "test" /// format!("hello {}", "world!"); // => "hello world!" /// format!("x = {}, y = {val}", 10, val = 30); // => "x = 10, y = 30" @@ -119,20 +120,13 @@ macro_rules! vec { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] +#[allow_internal_unstable(hint_must_use, liballoc_internals)] #[cfg_attr(not(test), rustc_diagnostic_item = "format_macro")] macro_rules! format { - ($($arg:tt)*) => {{ - let res = $crate::fmt::format($crate::__export::format_args!($($arg)*)); - res - }} -} - -/// Force AST node to an expression to improve diagnostics in pattern position. -#[doc(hidden)] -#[macro_export] -#[unstable(feature = "liballoc_internals", issue = "none", reason = "implementation detail")] -macro_rules! __rust_force_expr { - ($e:expr) => { - $e - }; + ($($arg:tt)*) => { + $crate::__export::must_use({ + let res = $crate::fmt::format($crate::__export::format_args!($($arg)*)); + res + }) + } } diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 1134c7f833e2b..5b84df9ecef30 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -1,10 +1,9 @@ #![unstable(feature = "raw_vec_internals", reason = "unstable const warnings", issue = "none")] use core::alloc::LayoutError; -use core::cmp; -use core::hint; use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties}; use core::ptr::{self, NonNull, Unique}; +use core::{cmp, hint}; #[cfg(not(no_global_oom_handling))] use crate::alloc::handle_alloc_error; @@ -52,7 +51,7 @@ impl Cap { /// * Produces `Unique::dangling()` on zero-length allocations. /// * Avoids freeing `Unique::dangling()`. /// * Catches all overflows in capacity computations (promotes them to "capacity overflow" panics). -/// * Guards against 32-bit systems allocating more than isize::MAX bytes. +/// * Guards against 32-bit systems allocating more than `isize::MAX` bytes. /// * Guards against overflowing your length. /// * Calls `handle_alloc_error` for fallible allocations. /// * Contains a `ptr::Unique` and thus endows the user with all related benefits. @@ -429,6 +428,7 @@ impl RawVec { /// /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] + #[inline] pub fn shrink_to_fit(&mut self, cap: usize) { if let Err(err) = self.shrink(cap) { handle_error(err); @@ -483,7 +483,7 @@ impl RawVec { // `finish_grow` is non-generic over `T`. let ptr = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?; - // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than isize::MAX items + // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items unsafe { self.set_ptr_and_cap(ptr, cap) }; Ok(()) } @@ -503,7 +503,7 @@ impl RawVec { // `finish_grow` is non-generic over `T`. let ptr = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?; - // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than isize::MAX items + // SAFETY: `finish_grow` would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items unsafe { self.set_ptr_and_cap(ptr, cap); } @@ -511,9 +511,25 @@ impl RawVec { } #[cfg(not(no_global_oom_handling))] + #[inline] fn shrink(&mut self, cap: usize) -> Result<(), TryReserveError> { assert!(cap <= self.capacity(), "Tried to shrink to a larger capacity"); + // SAFETY: Just checked this isn't trying to grow + unsafe { self.shrink_unchecked(cap) } + } + /// `shrink`, but without the capacity check. + /// + /// This is split out so that `shrink` can inline the check, since it + /// optimizes out in things like `shrink_to_fit`, without needing to + /// also inline all this code, as doing that ends up failing the + /// `vec-shrink-panic` codegen test when `shrink_to_fit` ends up being too + /// big for LLVM to be willing to inline. + /// + /// # Safety + /// `cap <= self.capacity()` + #[cfg(not(no_global_oom_handling))] + unsafe fn shrink_unchecked(&mut self, cap: usize) -> Result<(), TryReserveError> { let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) }; // See current_memory() why this assert is here const { assert!(mem::size_of::() % mem::align_of::() == 0) }; diff --git a/library/alloc/src/raw_vec/tests.rs b/library/alloc/src/raw_vec/tests.rs index 4194be530612d..48c6e5f46f8db 100644 --- a/library/alloc/src/raw_vec/tests.rs +++ b/library/alloc/src/raw_vec/tests.rs @@ -1,7 +1,8 @@ -use super::*; use core::mem::size_of; use std::cell::Cell; +use super::*; + #[test] fn allocator_param() { use crate::alloc::AllocError; diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 875c24c28e4a9..13d218e43a7af 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -241,25 +241,17 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[cfg(not(test))] -use crate::boxed::Box; -#[cfg(test)] -use std::boxed::Box; - use core::any::Any; -use core::borrow; use core::cell::Cell; +#[cfg(not(no_global_oom_handling))] +use core::clone::CloneToUninit; use core::cmp::Ordering; -use core::fmt; use core::hash::{Hash, Hasher}; -use core::hint; use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize}; -#[cfg(not(no_global_oom_handling))] -use core::mem::size_of_val; -use core::mem::{self, align_of_val_raw, forget, ManuallyDrop}; +use core::mem::{self, align_of_val_raw, ManuallyDrop}; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, Receiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; #[cfg(not(no_global_oom_handling))] @@ -267,13 +259,16 @@ use core::pin::Pin; use core::ptr::{self, drop_in_place, NonNull}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; +use core::{borrow, fmt, hint}; +#[cfg(test)] +use std::boxed::Box; #[cfg(not(no_global_oom_handling))] use crate::alloc::handle_alloc_error; -#[cfg(not(no_global_oom_handling))] -use crate::alloc::WriteCloneIntoRaw; use crate::alloc::{AllocError, Allocator, Global, Layout}; use crate::borrow::{Cow, ToOwned}; +#[cfg(not(test))] +use crate::boxed::Box; #[cfg(not(no_global_oom_handling))] use crate::string::String; #[cfg(not(no_global_oom_handling))] @@ -441,7 +436,7 @@ impl Rc { /// } /// /// impl Gadget { - /// /// Construct a reference counted Gadget. + /// /// Constructs a reference counted Gadget. /// fn new() -> Rc { /// // `me` is a `Weak` pointing at the new allocation of the /// // `Rc` we're constructing. @@ -451,7 +446,7 @@ impl Rc { /// }) /// } /// - /// /// Return a reference counted pointer to Self. + /// /// Returns a reference counted pointer to Self. /// fn me(&self) -> Rc { /// self.me.upgrade().unwrap() /// } @@ -667,16 +662,6 @@ impl Rc { } impl Rc { - /// Returns a reference to the underlying allocator. - /// - /// Note: this is an associated function, which means that you have - /// to call it as `Rc::allocator(&r)` instead of `r.allocator()`. This - /// is so that there is no conflict with a method on the inner type. - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn allocator(this: &Self) -> &A { - &this.alloc - } /// Constructs a new `Rc` in the provided allocator. /// /// # Examples @@ -920,19 +905,18 @@ impl Rc { #[stable(feature = "rc_unique", since = "1.4.0")] pub fn try_unwrap(this: Self) -> Result { if Rc::strong_count(&this) == 1 { - unsafe { - let val = ptr::read(&*this); // copy the contained object - let alloc = ptr::read(&this.alloc); // copy the allocator - - // Indicate to Weaks that they can't be promoted by decrementing - // the strong count, and then remove the implicit "strong weak" - // pointer while also handling drop logic by just crafting a - // fake Weak. - this.inner().dec_strong(); - let _weak = Weak { ptr: this.ptr, alloc }; - forget(this); - Ok(val) - } + let this = ManuallyDrop::new(this); + + let val: T = unsafe { ptr::read(&**this) }; // copy the contained object + let alloc: A = unsafe { ptr::read(&this.alloc) }; // copy the allocator + + // Indicate to Weaks that they can't be promoted by decrementing + // the strong count, and then remove the implicit "strong weak" + // pointer while also handling drop logic by just crafting a + // fake Weak. + this.inner().dec_strong(); + let _weak = Weak { ptr: this.ptr, alloc }; + Ok(val) } else { Err(this) } @@ -1289,6 +1273,8 @@ impl Rc { /// /// let five = Rc::from_raw(ptr); /// assert_eq!(2, Rc::strong_count(&five)); + /// # // Prevent leaks for Miri. + /// # Rc::decrement_strong_count(ptr); /// } /// ``` #[inline] @@ -1333,6 +1319,17 @@ impl Rc { } impl Rc { + /// Returns a reference to the underlying allocator. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Rc::allocator(&r)` instead of `r.allocator()`. This + /// is so that there is no conflict with a method on the inner type. + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn allocator(this: &Self) -> &A { + &this.alloc + } + /// Consumes the `Rc`, returning the wrapped pointer. /// /// To avoid a memory leak the pointer must be converted back to an `Rc` using @@ -1346,14 +1343,15 @@ impl Rc { /// let x = Rc::new("hello".to_owned()); /// let x_ptr = Rc::into_raw(x); /// assert_eq!(unsafe { &*x_ptr }, "hello"); + /// # // Prevent leaks for Miri. + /// # drop(unsafe { Rc::from_raw(x_ptr) }); /// ``` #[must_use = "losing the pointer will leak memory"] #[stable(feature = "rc_raw", since = "1.17.0")] #[rustc_never_returns_null_ptr] pub fn into_raw(this: Self) -> *const T { - let ptr = Self::as_ptr(&this); - mem::forget(this); - ptr + let this = ManuallyDrop::new(this); + Self::as_ptr(&*this) } /// Consumes the `Rc`, returning the wrapped pointer and allocator. @@ -1374,6 +1372,7 @@ impl Rc { /// let x = unsafe { Rc::from_raw_in(ptr, alloc) }; /// assert_eq!(&*x, "hello"); /// ``` + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { let this = mem::ManuallyDrop::new(this); @@ -1573,6 +1572,8 @@ impl Rc { /// /// let five = Rc::from_raw_in(ptr, System); /// assert_eq!(2, Rc::strong_count(&five)); + /// # // Prevent leaks for Miri. + /// # Rc::decrement_strong_count_in(ptr, System); /// } /// ``` #[inline] @@ -1751,7 +1752,8 @@ impl Rc { } } -impl Rc { +#[cfg(not(no_global_oom_handling))] +impl Rc { /// Makes a mutable reference into the given `Rc`. /// /// If there are other `Rc` pointers to the same allocation, then `make_mut` will @@ -1802,31 +1804,52 @@ impl Rc { /// assert!(76 == *data); /// assert!(weak.upgrade().is_none()); /// ``` - #[cfg(not(no_global_oom_handling))] #[inline] #[stable(feature = "rc_unique", since = "1.4.0")] pub fn make_mut(this: &mut Self) -> &mut T { + let size_of_val = size_of_val::(&**this); + if Rc::strong_count(this) != 1 { // Gotta clone the data, there are other Rcs. - // Pre-allocate memory to allow writing the cloned value directly. - let mut rc = Self::new_uninit_in(this.alloc.clone()); - unsafe { - let data = Rc::get_mut_unchecked(&mut rc); - (**this).write_clone_into_raw(data.as_mut_ptr()); - *this = rc.assume_init(); - } + + let this_data_ref: &T = &**this; + // `in_progress` drops the allocation if we panic before finishing initializing it. + let mut in_progress: UniqueRcUninit = + UniqueRcUninit::new(this_data_ref, this.alloc.clone()); + + // Initialize with clone of this. + let initialized_clone = unsafe { + // Clone. If the clone panics, `in_progress` will be dropped and clean up. + this_data_ref.clone_to_uninit(in_progress.data_ptr()); + // Cast type of pointer, now that it is initialized. + in_progress.into_rc() + }; + + // Replace `this` with newly constructed Rc. + *this = initialized_clone; } else if Rc::weak_count(this) != 0 { // Can just steal the data, all that's left is Weaks - let mut rc = Self::new_uninit_in(this.alloc.clone()); + + // We don't need panic-protection like the above branch does, but we might as well + // use the same mechanism. + let mut in_progress: UniqueRcUninit = + UniqueRcUninit::new(&**this, this.alloc.clone()); unsafe { - let data = Rc::get_mut_unchecked(&mut rc); - data.as_mut_ptr().copy_from_nonoverlapping(&**this, 1); + // Initialize `in_progress` with move of **this. + // We have to express this in terms of bytes because `T: ?Sized`; there is no + // operation that just copies a value based on its `size_of_val()`. + ptr::copy_nonoverlapping( + ptr::from_ref(&**this).cast::(), + in_progress.data_ptr().cast::(), + size_of_val, + ); this.inner().dec_strong(); // Remove implicit strong-weak ref (no need to craft a fake // Weak here -- we know other Weaks can clean up for us) this.inner().dec_weak(); - ptr::write(this, rc.assume_init()); + // Replace `this` with newly constructed Rc that has the moved data. + ptr::write(this, in_progress.into_rc()); } } // This unsafety is ok because we're guaranteed that the pointer @@ -1875,7 +1898,7 @@ impl Rc { } impl Rc { - /// Attempt to downcast the `Rc` to a concrete type. + /// Attempts to downcast the `Rc` to a concrete type. /// /// # Examples /// @@ -2100,7 +2123,7 @@ impl Rc<[T]> { } // All clear. Forget the guard so it doesn't free the new RcBox. - forget(guard); + mem::forget(guard); Self::from_ptr(ptr) } @@ -2252,7 +2275,7 @@ impl Default for Rc { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] impl Default for Rc { /// Creates an empty str inside an Rc /// @@ -2264,7 +2287,7 @@ impl Default for Rc { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] impl Default for Rc<[T]> { /// Creates an empty `[T]` inside an Rc /// @@ -2561,7 +2584,7 @@ impl From<[T; N]> for Rc<[T]> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "shared_from_slice", since = "1.21.0")] impl From<&[T]> for Rc<[T]> { - /// Allocate a reference-counted slice and fill it by cloning `v`'s items. + /// Allocates a reference-counted slice and fills it by cloning `v`'s items. /// /// # Example /// @@ -2580,7 +2603,7 @@ impl From<&[T]> for Rc<[T]> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "shared_from_slice", since = "1.21.0")] impl From<&str> for Rc { - /// Allocate a reference-counted string slice and copy `v` into it. + /// Allocates a reference-counted string slice and copies `v` into it. /// /// # Example /// @@ -2599,7 +2622,7 @@ impl From<&str> for Rc { #[cfg(not(no_global_oom_handling))] #[stable(feature = "shared_from_slice", since = "1.21.0")] impl From for Rc { - /// Allocate a reference-counted string slice and copy `v` into it. + /// Allocates a reference-counted string slice and copies `v` into it. /// /// # Example /// @@ -2637,7 +2660,7 @@ impl From> for Rc { #[cfg(not(no_global_oom_handling))] #[stable(feature = "shared_from_slice", since = "1.21.0")] impl From> for Rc<[T], A> { - /// Allocate a reference-counted slice and move `v`'s items into it. + /// Allocates a reference-counted slice and moves `v`'s items into it. /// /// # Example /// @@ -2670,8 +2693,8 @@ where B: ToOwned + ?Sized, Rc: From<&'a B> + From, { - /// Create a reference-counted pointer from - /// a clone-on-write pointer by copying its content. + /// Creates a reference-counted pointer from a clone-on-write pointer by + /// copying its content. /// /// # Example /// @@ -2974,6 +2997,13 @@ impl Weak { } impl Weak { + /// Returns a reference to the underlying allocator. + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn allocator(&self) -> &A { + &self.alloc + } + /// Returns a raw pointer to the object `T` pointed to by this `Weak`. /// /// The pointer is valid only if there are some strong references. The pointer may be dangling, @@ -3046,9 +3076,7 @@ impl Weak { #[must_use = "losing the pointer will leak memory"] #[stable(feature = "weak_into_raw", since = "1.45.0")] pub fn into_raw(self) -> *const T { - let result = self.as_ptr(); - mem::forget(self); - result + mem::ManuallyDrop::new(self).as_ptr() } /// Consumes the `Weak`, returning the wrapped pointer and allocator. @@ -3080,6 +3108,7 @@ impl Weak { /// /// [`from_raw_in`]: Weak::from_raw_in /// [`as_ptr`]: Weak::as_ptr + #[must_use = "losing the pointer will leak memory"] #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub fn into_raw_with_allocator(self) -> (*const T, A) { @@ -3496,7 +3525,7 @@ impl AsRef for Rc { #[stable(feature = "pin", since = "1.33.0")] impl Unpin for Rc {} -/// Get the offset within an `RcBox` for the payload behind a pointer. +/// Gets the offset within an `RcBox` for the payload behind a pointer. /// /// # Safety /// @@ -3518,7 +3547,7 @@ fn data_offset_align(align: usize) -> usize { layout.size() + layout.padding_needed_for(align) } -/// A uniquely owned `Rc` +/// A uniquely owned [`Rc`]. /// /// This represents an `Rc` that is known to be uniquely owned -- that is, have exactly one strong /// reference. Multiple weak pointers can be created, but attempts to upgrade those to strong @@ -3556,13 +3585,24 @@ fn data_offset_align(align: usize) -> usize { /// including fallible or async constructors. #[unstable(feature = "unique_rc_arc", issue = "112566")] #[derive(Debug)] -pub struct UniqueRc { +pub struct UniqueRc< + T: ?Sized, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { ptr: NonNull>, phantom: PhantomData>, + alloc: A, } +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl, U: ?Sized, A: Allocator> CoerceUnsized> + for UniqueRc +{ +} + +// Depends on A = Global impl UniqueRc { - /// Creates a new `UniqueRc` + /// Creates a new `UniqueRc`. /// /// Weak references to this `UniqueRc` can be created with [`UniqueRc::downgrade`]. Upgrading /// these weak references will fail before the `UniqueRc` has been converted into an [`Rc`]. @@ -3571,34 +3611,36 @@ impl UniqueRc { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "unique_rc_arc", issue = "112566")] pub fn new(value: T) -> Self { - Self { - ptr: Box::leak(Box::new(RcBox { + Self::new_in(value, Global) + } +} + +impl UniqueRc { + /// Creates a new `UniqueRc` in the provided allocator. + /// + /// Weak references to this `UniqueRc` can be created with [`UniqueRc::downgrade`]. Upgrading + /// these weak references will fail before the `UniqueRc` has been converted into an [`Rc`]. + /// After converting the `UniqueRc` into an [`Rc`], any weak references created beforehand will + /// point to the new [`Rc`]. + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "unique_rc_arc", issue = "112566")] + pub fn new_in(value: T, alloc: A) -> Self { + let (ptr, alloc) = Box::into_unique(Box::new_in( + RcBox { strong: Cell::new(0), // keep one weak reference so if all the weak pointers that are created are dropped // the UniqueRc still stays valid. weak: Cell::new(1), value, - })) - .into(), - phantom: PhantomData, - } - } - - /// Creates a new weak reference to the `UniqueRc` - /// - /// Attempting to upgrade this weak reference will fail before the `UniqueRc` has been converted - /// to a [`Rc`] using [`UniqueRc::into_rc`]. - #[unstable(feature = "unique_rc_arc", issue = "112566")] - pub fn downgrade(this: &Self) -> Weak { - // SAFETY: This pointer was allocated at creation time and we guarantee that we only have - // one strong reference before converting to a regular Rc. - unsafe { - this.ptr.as_ref().inc_weak(); - } - Weak { ptr: this.ptr, alloc: Global } + }, + alloc, + )); + Self { ptr: ptr.into(), phantom: PhantomData, alloc } } +} - /// Converts the `UniqueRc` into a regular [`Rc`] +impl UniqueRc { + /// Converts the `UniqueRc` into a regular [`Rc`]. /// /// This consumes the `UniqueRc` and returns a regular [`Rc`] that contains the `value` that /// is passed to `into_rc`. @@ -3606,19 +3648,41 @@ impl UniqueRc { /// Any weak references created before this method is called can now be upgraded to strong /// references. #[unstable(feature = "unique_rc_arc", issue = "112566")] - pub fn into_rc(this: Self) -> Rc { + pub fn into_rc(this: Self) -> Rc { let mut this = ManuallyDrop::new(this); + + // Move the allocator out. + // SAFETY: `this.alloc` will not be accessed again, nor dropped because it is in + // a `ManuallyDrop`. + let alloc: A = unsafe { ptr::read(&this.alloc) }; + // SAFETY: This pointer was allocated at creation time so we know it is valid. unsafe { // Convert our weak reference into a strong reference this.ptr.as_mut().strong.set(1); - Rc::from_inner(this.ptr) + Rc::from_inner_in(this.ptr, alloc) } } } +impl UniqueRc { + /// Creates a new weak reference to the `UniqueRc`. + /// + /// Attempting to upgrade this weak reference will fail before the `UniqueRc` has been converted + /// to a [`Rc`] using [`UniqueRc::into_rc`]. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + pub fn downgrade(this: &Self) -> Weak { + // SAFETY: This pointer was allocated at creation time and we guarantee that we only have + // one strong reference before converting to a regular Rc. + unsafe { + this.ptr.as_ref().inc_weak(); + } + Weak { ptr: this.ptr, alloc: this.alloc.clone() } + } +} + #[unstable(feature = "unique_rc_arc", issue = "112566")] -impl Deref for UniqueRc { +impl Deref for UniqueRc { type Target = T; fn deref(&self) -> &T { @@ -3628,7 +3692,7 @@ impl Deref for UniqueRc { } #[unstable(feature = "unique_rc_arc", issue = "112566")] -impl DerefMut for UniqueRc { +impl DerefMut for UniqueRc { fn deref_mut(&mut self) -> &mut T { // SAFETY: This pointer was allocated at creation time so we know it is valid. We know we // have unique ownership and therefore it's safe to make a mutable reference because @@ -3638,7 +3702,7 @@ impl DerefMut for UniqueRc { } #[unstable(feature = "unique_rc_arc", issue = "112566")] -unsafe impl<#[may_dangle] T> Drop for UniqueRc { +unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for UniqueRc { fn drop(&mut self) { unsafe { // destroy the contained object @@ -3648,8 +3712,73 @@ unsafe impl<#[may_dangle] T> Drop for UniqueRc { self.ptr.as_ref().dec_weak(); if self.ptr.as_ref().weak() == 0 { - Global.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())); + self.alloc.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())); } } } } + +/// A unique owning pointer to a [`RcBox`] **that does not imply the contents are initialized,** +/// but will deallocate it (without dropping the value) when dropped. +/// +/// This is a helper for [`Rc::make_mut()`] to ensure correct cleanup on panic. +/// It is nearly a duplicate of `UniqueRc, A>` except that it allows `T: !Sized`, +/// which `MaybeUninit` does not. +#[cfg(not(no_global_oom_handling))] +struct UniqueRcUninit { + ptr: NonNull>, + layout_for_value: Layout, + alloc: Option, +} + +#[cfg(not(no_global_oom_handling))] +impl UniqueRcUninit { + /// Allocates a RcBox with layout suitable to contain `for_value` or a clone of it. + fn new(for_value: &T, alloc: A) -> UniqueRcUninit { + let layout = Layout::for_value(for_value); + let ptr = unsafe { + Rc::allocate_for_layout( + layout, + |layout_for_rcbox| alloc.allocate(layout_for_rcbox), + |mem| mem.with_metadata_of(ptr::from_ref(for_value) as *const RcBox), + ) + }; + Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) } + } + + /// Returns the pointer to be written into to initialize the [`Rc`]. + fn data_ptr(&mut self) -> *mut T { + let offset = data_offset_align(self.layout_for_value.align()); + unsafe { self.ptr.as_ptr().byte_add(offset) as *mut T } + } + + /// Upgrade this into a normal [`Rc`]. + /// + /// # Safety + /// + /// The data must have been initialized (by writing to [`Self::data_ptr()`]). + unsafe fn into_rc(self) -> Rc { + let mut this = ManuallyDrop::new(self); + let ptr = this.ptr; + let alloc = this.alloc.take().unwrap(); + + // SAFETY: The pointer is valid as per `UniqueRcUninit::new`, and the caller is responsible + // for having initialized the data. + unsafe { Rc::from_ptr_in(ptr.as_ptr(), alloc) } + } +} + +#[cfg(not(no_global_oom_handling))] +impl Drop for UniqueRcUninit { + fn drop(&mut self) { + // SAFETY: + // * new() produced a pointer safe to deallocate. + // * We own the pointer unless into_rc() was called, which forgets us. + unsafe { + self.alloc + .take() + .unwrap() + .deallocate(self.ptr.cast(), rcbox_layout_for_value_layout(self.layout_for_value)); + } + } +} diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs index c8a40603d9db2..84e8b325f71fc 100644 --- a/library/alloc/src/rc/tests.rs +++ b/library/alloc/src/rc/tests.rs @@ -1,8 +1,8 @@ -use super::*; - use std::cell::RefCell; use std::clone::Clone; +use super::*; + #[test] fn test_clone() { let x = Rc::new(RefCell::new(5)); @@ -316,6 +316,24 @@ fn test_cowrc_clone_weak() { assert!(cow1_weak.upgrade().is_none()); } +/// This is similar to the doc-test for `Rc::make_mut()`, but on an unsized type (slice). +#[test] +fn test_cowrc_unsized() { + use std::rc::Rc; + + let mut data: Rc<[i32]> = Rc::new([10, 20, 30]); + + Rc::make_mut(&mut data)[0] += 1; // Won't clone anything + let mut other_data = Rc::clone(&data); // Won't clone inner data + Rc::make_mut(&mut data)[1] += 1; // Clones inner data + Rc::make_mut(&mut data)[2] += 1; // Won't clone anything + Rc::make_mut(&mut other_data)[0] *= 10; // Won't clone anything + + // Now `data` and `other_data` point to different allocations. + assert_eq!(*data, [11, 21, 31]); + assert_eq!(*other_data, [110, 20, 30]); +} + #[test] fn test_show() { let foo = Rc::new(75); @@ -606,6 +624,23 @@ fn test_unique_rc_drops_contents() { assert!(dropped); } +/// Exercise the non-default allocator usage. +#[test] +fn test_unique_rc_with_alloc_drops_contents() { + let mut dropped = false; + struct DropMe<'a>(&'a mut bool); + impl Drop for DropMe<'_> { + fn drop(&mut self) { + *self.0 = true; + } + } + { + let rc = UniqueRc::new_in(DropMe(&mut dropped), std::alloc::System); + drop(rc); + } + assert!(dropped); +} + #[test] fn test_unique_rc_weak_clone_holding_ref() { let mut v = UniqueRc::new(0u8); @@ -614,3 +649,12 @@ fn test_unique_rc_weak_clone_holding_ref() { let _ = w.clone(); // touch weak count *r = 123; } + +#[test] +fn test_unique_rc_unsizing_coercion() { + let mut rc: UniqueRc<[u8]> = UniqueRc::new([0u8; 3]); + assert_eq!(rc.len(), 3); + rc[0] = 123; + let rc: Rc<[u8]> = UniqueRc::into_rc(rc); + assert_eq!(*rc, [123, 0, 0]); +} diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index ebe6f7e7caa9b..7dcf344cdc5e0 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -16,7 +16,7 @@ use core::borrow::{Borrow, BorrowMut}; #[cfg(not(no_global_oom_handling))] use core::cmp::Ordering::{self, Less}; #[cfg(not(no_global_oom_handling))] -use core::mem::{self, SizedTypeProperties}; +use core::mem::{self, MaybeUninit}; #[cfg(not(no_global_oom_handling))] use core::ptr; #[cfg(not(no_global_oom_handling))] @@ -24,7 +24,7 @@ use core::slice::sort; use crate::alloc::Allocator; #[cfg(not(no_global_oom_handling))] -use crate::alloc::{self, Global}; +use crate::alloc::Global; #[cfg(not(no_global_oom_handling))] use crate::borrow::ToOwned; use crate::boxed::Box; @@ -78,7 +78,6 @@ pub use core::slice::{SplitInclusive, SplitInclusiveMut}; // N.B., see the `hack` module in this file for more details. #[cfg(test)] pub use hack::into_vec; - // HACK(japaric) needed for the implementation of `Vec::clone` during testing // N.B., see the `hack` module in this file for more details. #[cfg(test)] @@ -174,23 +173,32 @@ pub(crate) mod hack { #[cfg(not(test))] impl [T] { - /// Sorts the slice. + /// Sorts the slice, preserving initial order of equal elements. + /// + /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) + /// worst-case. /// - /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) worst-case. + /// If `T: Ord` does not implement a total order the resulting order is unspecified. All + /// original elements will remain in the slice and any possible modifications via interior + /// mutability are observed in the input. Same is true if `T: Ord` panics. /// /// When applicable, unstable sorting is preferred because it is generally faster than stable - /// sorting and it doesn't allocate auxiliary memory. - /// See [`sort_unstable`](slice::sort_unstable). + /// sorting and it doesn't allocate auxiliary memory. See + /// [`sort_unstable`](slice::sort_unstable). The exception are partially sorted slices, which + /// may be better served with `slice::sort`. /// /// # Current implementation /// - /// The current algorithm is an adaptive, iterative merge sort inspired by - /// [timsort](https://en.wikipedia.org/wiki/Timsort). - /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of - /// two or more sorted sequences concatenated one after another. + /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which + /// combines the fast average case of quicksort with the fast worst case and partial run + /// detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs + /// with k distinct elements, the expected time to sort the data is *O*(*n* \* log(*k*)). + /// + /// The auxiliary memory allocation behavior depends on the input length. Short slices are + /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it + /// clamps at `self.len() / 2`. /// - /// Also, it allocates temporary storage half the size of `self`, but for short slices a - /// non-allocating insertion sort is used instead. + /// If `T: Ord` does not implement a total order, the implementation may panic. /// /// # Examples /// @@ -200,6 +208,8 @@ impl [T] { /// v.sort(); /// assert!(v == [-5, -3, 1, 2, 4]); /// ``` + /// + /// [driftsort]: https://github.com/Voultapher/driftsort #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] @@ -211,13 +221,18 @@ impl [T] { stable_sort(self, T::lt); } - /// Sorts the slice with a comparator function. + /// Sorts the slice with a comparator function, preserving initial order of equal elements. + /// + /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) + /// worst-case. /// - /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) worst-case. + /// The comparator function should define a total ordering for the elements in the slice. If the + /// ordering is not total, the order of the elements is unspecified. /// - /// The comparator function must define a total ordering for the elements in the slice. If - /// the ordering is not total, the order of the elements is unspecified. An order is a - /// total order if it is (for all `a`, `b` and `c`): + /// If the comparator function does not implement a total order the resulting order is + /// unspecified. All original elements will remain in the slice and any possible modifications + /// via interior mutability are observed in the input. Same is true if the comparator function + /// panics. A total order (for all `a`, `b` and `c`): /// /// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and /// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. @@ -227,23 +242,22 @@ impl [T] { /// /// ``` /// let mut floats = [5f64, 4.0, 1.0, 3.0, 2.0]; - /// floats.sort_by(|a, b| a.partial_cmp(b).unwrap()); + /// floats.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); /// assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 5.0]); /// ``` /// - /// When applicable, unstable sorting is preferred because it is generally faster than stable - /// sorting and it doesn't allocate auxiliary memory. - /// See [`sort_unstable_by`](slice::sort_unstable_by). - /// /// # Current implementation /// - /// The current algorithm is an adaptive, iterative merge sort inspired by - /// [timsort](https://en.wikipedia.org/wiki/Timsort). - /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of - /// two or more sorted sequences concatenated one after another. + /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which + /// combines the fast average case of quicksort with the fast worst case and partial run + /// detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs + /// with k distinct elements, the expected time to sort the data is *O*(*n* \* log(*k*)). /// - /// Also, it allocates temporary storage half the size of `self`, but for short slices a - /// non-allocating insertion sort is used instead. + /// The auxiliary memory allocation behavior depends on the input length. Short slices are + /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it + /// clamps at `self.len() / 2`. + /// + /// If `T: Ord` does not implement a total order, the implementation may panic. /// /// # Examples /// @@ -256,6 +270,8 @@ impl [T] { /// v.sort_by(|a, b| b.cmp(a)); /// assert!(v == [5, 4, 3, 2, 1]); /// ``` + /// + /// [driftsort]: https://github.com/Voultapher/driftsort #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] @@ -267,28 +283,27 @@ impl [T] { stable_sort(self, |a, b| compare(a, b) == Less); } - /// Sorts the slice with a key extraction function. + /// Sorts the slice with a key extraction function, preserving initial order of equal elements. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* \* log(*n*)) /// worst-case, where the key function is *O*(*m*). /// - /// For expensive key functions (e.g. functions that are not simple property accesses or - /// basic operations), [`sort_by_cached_key`](slice::sort_by_cached_key) is likely to be - /// significantly faster, as it does not recompute element keys. - /// - /// When applicable, unstable sorting is preferred because it is generally faster than stable - /// sorting and it doesn't allocate auxiliary memory. - /// See [`sort_unstable_by_key`](slice::sort_unstable_by_key). + /// If `K: Ord` does not implement a total order the resulting order is unspecified. + /// All original elements will remain in the slice and any possible modifications via interior + /// mutability are observed in the input. Same is true if `K: Ord` panics. /// /// # Current implementation /// - /// The current algorithm is an adaptive, iterative merge sort inspired by - /// [timsort](https://en.wikipedia.org/wiki/Timsort). - /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of - /// two or more sorted sequences concatenated one after another. + /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which + /// combines the fast average case of quicksort with the fast worst case and partial run + /// detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs + /// with k distinct elements, the expected time to sort the data is *O*(*n* \* log(*k*)). /// - /// Also, it allocates temporary storage half the size of `self`, but for short slices a - /// non-allocating insertion sort is used instead. + /// The auxiliary memory allocation behavior depends on the input length. Short slices are + /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it + /// clamps at `self.len() / 2`. + /// + /// If `K: Ord` does not implement a total order, the implementation may panic. /// /// # Examples /// @@ -298,6 +313,8 @@ impl [T] { /// v.sort_by_key(|k| k.abs()); /// assert!(v == [1, 2, -3, 4, -5]); /// ``` + /// + /// [driftsort]: https://github.com/Voultapher/driftsort #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "slice_sort_by_key", since = "1.7.0")] @@ -310,27 +327,30 @@ impl [T] { stable_sort(self, |a, b| f(a).lt(&f(b))); } - /// Sorts the slice with a key extraction function. + /// Sorts the slice with a key extraction function, preserving initial order of equal elements. /// - /// During sorting, the key function is called at most once per element, by using - /// temporary storage to remember the results of key evaluation. - /// The order of calls to the key function is unspecified and may change in future versions - /// of the standard library. + /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* + *n* \* + /// log(*n*)) worst-case, where the key function is *O*(*m*). /// - /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* + *n* \* log(*n*)) - /// worst-case, where the key function is *O*(*m*). + /// During sorting, the key function is called at most once per element, by using temporary + /// storage to remember the results of key evaluation. The order of calls to the key function is + /// unspecified and may change in future versions of the standard library. + /// + /// If `K: Ord` does not implement a total order the resulting order is unspecified. + /// All original elements will remain in the slice and any possible modifications via interior + /// mutability are observed in the input. Same is true if `K: Ord` panics. /// - /// For simple key functions (e.g., functions that are property accesses or - /// basic operations), [`sort_by_key`](slice::sort_by_key) is likely to be - /// faster. + /// For simple key functions (e.g., functions that are property accesses or basic operations), + /// [`sort_by_key`](slice::sort_by_key) is likely to be faster. /// /// # Current implementation /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. + /// The current implementation is based on [instruction-parallel-network sort][ipnsort] by Lukas + /// Bergdoll, which combines the fast average case of randomized quicksort with the fast worst + /// case of heapsort, while achieving linear time on fully sorted and reversed inputs. And + /// *O*(*k* \* log(*n*)) where *k* is the number of distinct elements in the input. It leverages + /// superscalar out-of-order execution capabilities commonly found in CPUs, to efficiently + /// perform the operation. /// /// In the worst case, the algorithm allocates temporary storage in a `Vec<(K, usize)>` the /// length of the slice. @@ -344,7 +364,7 @@ impl [T] { /// assert!(v == [-3, -5, 2, 32, 4]); /// ``` /// - /// [pdqsort]: https://github.com/orlp/pdqsort + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "slice_sort_by_cached_key", since = "1.34.0")] @@ -361,7 +381,7 @@ impl [T] { $slice.iter().map($f).enumerate().map(|(i, k)| (k, i as $t)).collect(); // The elements of `indices` are unique, as they are indexed, so any sort will be // stable with respect to the original slice. We use `sort_unstable` here because - // it requires less memory allocation. + // it requires no memory allocation. indices.sort_unstable(); for i in 0..$slice.len() { let mut index = indices[i].1; @@ -374,24 +394,24 @@ impl [T] { }}; } - let sz_u8 = mem::size_of::<(K, u8)>(); - let sz_u16 = mem::size_of::<(K, u16)>(); - let sz_u32 = mem::size_of::<(K, u32)>(); - let sz_usize = mem::size_of::<(K, usize)>(); - let len = self.len(); if len < 2 { return; } - if sz_u8 < sz_u16 && len <= (u8::MAX as usize) { - return sort_by_key!(u8, self, f); - } - if sz_u16 < sz_u32 && len <= (u16::MAX as usize) { - return sort_by_key!(u16, self, f); - } - if sz_u32 < sz_usize && len <= (u32::MAX as usize) { + + // Avoids binary-size usage in cases where the alignment doesn't work out to make this + // beneficial or on 32-bit platforms. + let is_using_u32_as_idx_type_helpful = + const { mem::size_of::<(K, u32)>() < mem::size_of::<(K, usize)>() }; + + // It's possible to instantiate this for u8 and u16 but, doing so is very wasteful in terms + // of compile-times and binary-size, the peak saved heap memory for u16 is (u8 + u16) -> 4 + // bytes * u16::MAX vs (u8 + u32) -> 8 bytes * u16::MAX, the saved heap memory is at peak + // ~262KB. + if is_using_u32_as_idx_type_helpful && len <= (u32::MAX as usize) { return sort_by_key!(u32, self, f); } + sort_by_key!(usize, self, f) } @@ -843,46 +863,17 @@ fn stable_sort(v: &mut [T], mut is_less: F) where F: FnMut(&T, &T) -> bool, { - if T::IS_ZST { - // Sorting has no meaningful behavior on zero-sized types. Do nothing. - return; - } - - let elem_alloc_fn = |len: usize| -> *mut T { - // SAFETY: Creating the layout is safe as long as merge_sort never calls this with len > - // v.len(). Alloc in general will only be used as 'shadow-region' to store temporary swap - // elements. - unsafe { alloc::alloc(alloc::Layout::array::(len).unwrap_unchecked()) as *mut T } - }; - - let elem_dealloc_fn = |buf_ptr: *mut T, len: usize| { - // SAFETY: Creating the layout is safe as long as merge_sort never calls this with len > - // v.len(). The caller must ensure that buf_ptr was created by elem_alloc_fn with the same - // len. - unsafe { - alloc::dealloc(buf_ptr as *mut u8, alloc::Layout::array::(len).unwrap_unchecked()); - } - }; - - let run_alloc_fn = |len: usize| -> *mut sort::TimSortRun { - // SAFETY: Creating the layout is safe as long as merge_sort never calls this with an - // obscene length or 0. - unsafe { - alloc::alloc(alloc::Layout::array::(len).unwrap_unchecked()) - as *mut sort::TimSortRun - } - }; + sort::stable::sort::>(v, &mut is_less); +} - let run_dealloc_fn = |buf_ptr: *mut sort::TimSortRun, len: usize| { - // SAFETY: The caller must ensure that buf_ptr was created by elem_alloc_fn with the same - // len. - unsafe { - alloc::dealloc( - buf_ptr as *mut u8, - alloc::Layout::array::(len).unwrap_unchecked(), - ); - } - }; +#[cfg(not(no_global_oom_handling))] +#[unstable(issue = "none", feature = "std_internals")] +impl sort::stable::BufGuard for Vec { + fn with_capacity(capacity: usize) -> Self { + Vec::with_capacity(capacity) + } - sort::merge_sort(v, &mut is_less, elem_alloc_fn, elem_dealloc_fn, run_alloc_fn, run_dealloc_fn); + fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit] { + self.spare_capacity_mut() + } } diff --git a/library/alloc/src/slice/tests.rs b/library/alloc/src/slice/tests.rs index 54bc4e77b16f0..786704caeb0ad 100644 --- a/library/alloc/src/slice/tests.rs +++ b/library/alloc/src/slice/tests.rs @@ -1,18 +1,21 @@ +use core::cell::Cell; +use core::cmp::Ordering::{self, Equal, Greater, Less}; +use core::convert::identity; +use core::sync::atomic::AtomicUsize; +use core::sync::atomic::Ordering::Relaxed; +use core::{fmt, mem}; +use std::panic; + +use rand::distributions::Standard; +use rand::prelude::*; +use rand::{Rng, RngCore}; + use crate::borrow::ToOwned; use crate::rc::Rc; use crate::string::ToString; use crate::test_helpers::test_rng; use crate::vec::Vec; -use core::cell::Cell; -use core::cmp::Ordering::{self, Equal, Greater, Less}; -use core::convert::identity; -use core::fmt; -use core::mem; -use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; -use rand::{distributions::Standard, prelude::*, Rng, RngCore}; -use std::panic; - macro_rules! do_test { ($input:ident, $func:ident) => { let len = $input.len(); @@ -34,7 +37,7 @@ macro_rules! do_test { } let v = $input.to_owned(); - let _ = std::panic::catch_unwind(move || { + let _ = panic::catch_unwind(move || { let mut v = v; let mut panic_countdown = panic_countdown; v.$func(|a, b| { @@ -240,6 +243,7 @@ fn panic_safe() { #[test] #[cfg_attr(miri, ignore)] // Miri is too slow +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_sort() { let mut rng = test_rng(); @@ -294,15 +298,20 @@ fn test_sort() { } } - // Sort using a completely random comparison function. - // This will reorder the elements *somehow*, but won't panic. - let mut v = [0; 500]; - for i in 0..v.len() { + const ORD_VIOLATION_MAX_LEN: usize = 500; + let mut v = [0; ORD_VIOLATION_MAX_LEN]; + for i in 0..ORD_VIOLATION_MAX_LEN { v[i] = i as i32; } - v.sort_by(|_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); + + // Sort using a completely random comparison function. This will reorder the elements *somehow*, + // it may panic but the original elements must still be present. + let _ = panic::catch_unwind(move || { + v.sort_by(|_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); + }); + v.sort(); - for i in 0..v.len() { + for i in 0..ORD_VIOLATION_MAX_LEN { assert_eq!(v[i], i as i32); } diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 3e23612d0c13c..d7fba3ae159c6 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -9,19 +9,9 @@ use core::borrow::{Borrow, BorrowMut}; use core::iter::FusedIterator; -use core::mem; -use core::ptr; -use core::str::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; -use core::unicode::conversions; - -use crate::borrow::ToOwned; -use crate::boxed::Box; -use crate::slice::{Concat, Join, SliceIndex}; -use crate::string::String; -use crate::vec::Vec; - #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::pattern; +use core::str::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; #[stable(feature = "encode_utf16", since = "1.8.0")] pub use core::str::EncodeUtf16; #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] @@ -55,6 +45,14 @@ pub use core::str::{RSplitN, SplitN}; pub use core::str::{RSplitTerminator, SplitTerminator}; #[stable(feature = "utf8_chunks", since = "1.79.0")] pub use core::str::{Utf8Chunk, Utf8Chunks}; +use core::unicode::conversions; +use core::{mem, ptr}; + +use crate::borrow::ToOwned; +use crate::boxed::Box; +use crate::slice::{Concat, Join, SliceIndex}; +use crate::string::String; +use crate::vec::Vec; /// Note: `str` in `Concat` is not meaningful here. /// This type parameter of the trait only exists to enable another impl. @@ -206,15 +204,16 @@ impl BorrowMut for String { #[stable(feature = "rust1", since = "1.0.0")] impl ToOwned for str { type Owned = String; + #[inline] fn to_owned(&self) -> String { unsafe { String::from_utf8_unchecked(self.as_bytes().to_owned()) } } + #[inline] fn clone_into(&self, target: &mut String) { - let mut b = mem::take(target).into_bytes(); - self.as_bytes().clone_into(&mut b); - *target = unsafe { String::from_utf8_unchecked(b) } + target.clear(); + target.push_str(self); } } @@ -268,7 +267,7 @@ impl str { without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn replace<'a, P: Pattern<'a>>(&'a self, from: P, to: &str) -> String { + pub fn replace(&self, from: P, to: &str) -> String { let mut result = String::new(); let mut last_end = 0; for (start, part) in self.match_indices(from) { @@ -308,7 +307,7 @@ impl str { #[must_use = "this returns the replaced string as a new allocation, \ without modifying the original"] #[stable(feature = "str_replacen", since = "1.16.0")] - pub fn replacen<'a, P: Pattern<'a>>(&'a self, pat: P, to: &str, count: usize) -> String { + pub fn replacen(&self, pat: P, to: &str, count: usize) -> String { // Hope to reduce the times of re-allocation let mut result = String::with_capacity(32); let mut last_end = 0; diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 36078da7c35a6..124230812df56 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -43,8 +43,6 @@ #![stable(feature = "rust1", since = "1.0.0")] use core::error::Error; -use core::fmt; -use core::hash; #[cfg(not(no_global_oom_handling))] use core::iter::from_fn; use core::iter::FusedIterator; @@ -55,9 +53,8 @@ use core::ops::AddAssign; #[cfg(not(no_global_oom_handling))] use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{self, Range, RangeBounds}; -use core::ptr; -use core::slice; use core::str::pattern::Pattern; +use core::{fmt, hash, ptr, slice}; #[cfg(not(no_global_oom_handling))] use crate::alloc::Allocator; @@ -903,7 +900,7 @@ impl String { /// let rebuilt = unsafe { String::from_raw_parts(ptr, len, cap) }; /// assert_eq!(rebuilt, "hello"); /// ``` - #[must_use = "`self` will be dropped if the result is not used"] + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_raw_parts(self) -> (*mut u8, usize, usize) { self.vec.into_raw_parts() @@ -1497,10 +1494,7 @@ impl String { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "string_remove_matches", reason = "new API", issue = "72826")] - pub fn remove_matches<'a, P>(&'a mut self, pat: P) - where - P: for<'x> Pattern<'x>, - { + pub fn remove_matches(&mut self, pat: P) { use core::str::pattern::Searcher; let rejections = { @@ -1984,6 +1978,9 @@ impl String { /// let x = String::from("bucket"); /// let static_ref: &'static mut str = x.leak(); /// assert_eq!(static_ref, "bucket"); + /// # // FIXME(https://github.com/rust-lang/miri/issues/3670): + /// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak. + /// # drop(unsafe { Box::from_raw(static_ref) }); /// ``` #[stable(feature = "string_leak", since = "1.72.0")] #[inline] @@ -2285,35 +2282,41 @@ impl<'a> Extend> for String { reason = "API not fully fleshed out and ready to be stabilized", issue = "27721" )] -impl<'a, 'b> Pattern<'a> for &'b String { - type Searcher = <&'b str as Pattern<'a>>::Searcher; +impl<'b> Pattern for &'b String { + type Searcher<'a> = <&'b str as Pattern>::Searcher<'a>; - fn into_searcher(self, haystack: &'a str) -> <&'b str as Pattern<'a>>::Searcher { + fn into_searcher(self, haystack: &str) -> <&'b str as Pattern>::Searcher<'_> { self[..].into_searcher(haystack) } #[inline] - fn is_contained_in(self, haystack: &'a str) -> bool { + fn is_contained_in(self, haystack: &str) -> bool { self[..].is_contained_in(haystack) } #[inline] - fn is_prefix_of(self, haystack: &'a str) -> bool { + fn is_prefix_of(self, haystack: &str) -> bool { self[..].is_prefix_of(haystack) } #[inline] - fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_prefix_of(self, haystack: &str) -> Option<&str> { self[..].strip_prefix_of(haystack) } #[inline] - fn is_suffix_of(self, haystack: &'a str) -> bool { + fn is_suffix_of<'a>(self, haystack: &'a str) -> bool + where + Self::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>, + { self[..].is_suffix_of(haystack) } #[inline] - fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&str> + where + Self::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>, + { self[..].strip_suffix_of(haystack) } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 7dcaa59dcd1c7..3ad0dae77dbde 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -9,18 +9,15 @@ //! `#[cfg(target_has_atomic = "ptr")]`. use core::any::Any; -use core::borrow; +#[cfg(not(no_global_oom_handling))] +use core::clone::CloneToUninit; use core::cmp::Ordering; -use core::fmt; use core::hash::{Hash, Hasher}; -use core::hint; use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize}; -#[cfg(not(no_global_oom_handling))] -use core::mem::size_of_val; -use core::mem::{self, align_of_val_raw}; +use core::mem::{self, align_of_val_raw, ManuallyDrop}; use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, Receiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::pin::Pin; @@ -29,11 +26,10 @@ use core::ptr::{self, NonNull}; use core::slice::from_raw_parts_mut; use core::sync::atomic; use core::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use core::{borrow, fmt, hint}; #[cfg(not(no_global_oom_handling))] use crate::alloc::handle_alloc_error; -#[cfg(not(no_global_oom_handling))] -use crate::alloc::WriteCloneIntoRaw; use crate::alloc::{AllocError, Allocator, Global, Layout}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; @@ -199,11 +195,7 @@ macro_rules! acquire { /// /// Sharing some immutable data between threads: /// -// Note that we **do not** run these tests here. The windows builders get super -// unhappy if a thread outlives the main thread and then exits at the same time -// (something deadlocks) so we just avoid this entirely by not running these -// tests. -/// ```no_run +/// ``` /// use std::sync::Arc; /// use std::thread; /// @@ -222,7 +214,7 @@ macro_rules! acquire { /// /// [`AtomicUsize`]: core::sync::atomic::AtomicUsize "sync::atomic::AtomicUsize" /// -/// ```no_run +/// ``` /// use std::sync::Arc; /// use std::sync::atomic::{AtomicUsize, Ordering}; /// use std::thread; @@ -434,7 +426,7 @@ impl Arc { /// } /// /// impl Gadget { - /// /// Construct a reference counted Gadget. + /// /// Constructs a reference counted Gadget. /// fn new() -> Arc { /// // `me` is a `Weak` pointing at the new allocation of the /// // `Arc` we're constructing. @@ -444,7 +436,7 @@ impl Arc { /// }) /// } /// - /// /// Return a reference counted pointer to Self. + /// /// Returns a reference counted pointer to Self. /// fn me(&self) -> Arc { /// self.me.upgrade().unwrap() /// } @@ -683,16 +675,6 @@ impl Arc { } impl Arc { - /// Returns a reference to the underlying allocator. - /// - /// Note: this is an associated function, which means that you have - /// to call it as `Arc::allocator(&a)` instead of `a.allocator()`. This - /// is so that there is no conflict with a method on the inner type. - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn allocator(this: &Self) -> &A { - &this.alloc - } /// Constructs a new `Arc` in the provided allocator. /// /// # Examples @@ -942,15 +924,18 @@ impl Arc { /// This will succeed even if there are outstanding weak references. /// /// It is strongly recommended to use [`Arc::into_inner`] instead if you don't - /// want to keep the `Arc` in the [`Err`] case. - /// Immediately dropping the [`Err`] payload, like in the expression - /// `Arc::try_unwrap(this).ok()`, can still cause the strong count to - /// drop to zero and the inner value of the `Arc` to be dropped: - /// For instance if two threads each execute this expression in parallel, then - /// there is a race condition. The threads could first both check whether they - /// have the last clone of their `Arc` via `Arc::try_unwrap`, and then - /// both drop their `Arc` in the call to [`ok`][`Result::ok`], - /// taking the strong count from two down to zero. + /// keep the `Arc` in the [`Err`] case. + /// Immediately dropping the [`Err`]-value, as the expression + /// `Arc::try_unwrap(this).ok()` does, can cause the strong count to + /// drop to zero and the inner value of the `Arc` to be dropped. + /// For instance, if two threads execute such an expression in parallel, + /// there is a race condition without the possibility of unsafety: + /// The threads could first both check whether they own the last instance + /// in `Arc::try_unwrap`, determine that they both do not, and then both + /// discard and drop their instance in the call to [`ok`][`Result::ok`]. + /// In this scenario, the value inside the `Arc` is safely destroyed + /// by exactly one of the threads, but neither thread will ever be able + /// to use the value. /// /// # Examples /// @@ -973,16 +958,14 @@ impl Arc { acquire!(this.inner().strong); - unsafe { - let elem = ptr::read(&this.ptr.as_ref().data); - let alloc = ptr::read(&this.alloc); // copy the allocator + let this = ManuallyDrop::new(this); + let elem: T = unsafe { ptr::read(&this.ptr.as_ref().data) }; + let alloc: A = unsafe { ptr::read(&this.alloc) }; // copy the allocator - // Make a weak pointer to clean up the implicit strong-weak reference - let _weak = Weak { ptr: this.ptr, alloc }; - mem::forget(this); + // Make a weak pointer to clean up the implicit strong-weak reference + let _weak = Weak { ptr: this.ptr, alloc }; - Ok(elem) - } + Ok(elem) } /// Returns the inner value, if the `Arc` has exactly one strong reference. @@ -1427,6 +1410,8 @@ impl Arc { /// // the `Arc` between threads. /// let five = Arc::from_raw(ptr); /// assert_eq!(2, Arc::strong_count(&five)); + /// # // Prevent leaks for Miri. + /// # Arc::decrement_strong_count(ptr); /// } /// ``` #[inline] @@ -1473,6 +1458,17 @@ impl Arc { } impl Arc { + /// Returns a reference to the underlying allocator. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Arc::allocator(&a)` instead of `a.allocator()`. This + /// is so that there is no conflict with a method on the inner type. + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn allocator(this: &Self) -> &A { + &this.alloc + } + /// Consumes the `Arc`, returning the wrapped pointer. /// /// To avoid a memory leak the pointer must be converted back to an `Arc` using @@ -1486,14 +1482,15 @@ impl Arc { /// let x = Arc::new("hello".to_owned()); /// let x_ptr = Arc::into_raw(x); /// assert_eq!(unsafe { &*x_ptr }, "hello"); + /// # // Prevent leaks for Miri. + /// # drop(unsafe { Arc::from_raw(x_ptr) }); /// ``` #[must_use = "losing the pointer will leak memory"] #[stable(feature = "rc_raw", since = "1.17.0")] #[rustc_never_returns_null_ptr] pub fn into_raw(this: Self) -> *const T { - let ptr = Self::as_ptr(&this); - mem::forget(this); - ptr + let this = ManuallyDrop::new(this); + Self::as_ptr(&*this) } /// Consumes the `Arc`, returning the wrapped pointer and allocator. @@ -1768,6 +1765,8 @@ impl Arc { /// // the `Arc` between threads. /// let five = Arc::from_raw_in(ptr, System); /// assert_eq!(2, Arc::strong_count(&five)); + /// # // Prevent leaks for Miri. + /// # Arc::decrement_strong_count_in(ptr, System); /// } /// ``` #[inline] @@ -2149,7 +2148,8 @@ unsafe impl DerefPure for Arc {} #[unstable(feature = "receiver_trait", issue = "none")] impl Receiver for Arc {} -impl Arc { +#[cfg(not(no_global_oom_handling))] +impl Arc { /// Makes a mutable reference into the given `Arc`. /// /// If there are other `Arc` pointers to the same allocation, then `make_mut` will @@ -2200,10 +2200,11 @@ impl Arc { /// assert!(76 == *data); /// assert!(weak.upgrade().is_none()); /// ``` - #[cfg(not(no_global_oom_handling))] #[inline] #[stable(feature = "arc_unique", since = "1.4.0")] pub fn make_mut(this: &mut Self) -> &mut T { + let size_of_val = mem::size_of_val::(&**this); + // Note that we hold both a strong reference and a weak reference. // Thus, releasing our strong reference only will not, by itself, cause // the memory to be deallocated. @@ -2214,13 +2215,19 @@ impl Arc { // deallocated. if this.inner().strong.compare_exchange(1, 0, Acquire, Relaxed).is_err() { // Another strong pointer exists, so we must clone. - // Pre-allocate memory to allow writing the cloned value directly. - let mut arc = Self::new_uninit_in(this.alloc.clone()); - unsafe { - let data = Arc::get_mut_unchecked(&mut arc); - (**this).write_clone_into_raw(data.as_mut_ptr()); - *this = arc.assume_init(); - } + + let this_data_ref: &T = &**this; + // `in_progress` drops the allocation if we panic before finishing initializing it. + let mut in_progress: UniqueArcUninit = + UniqueArcUninit::new(this_data_ref, this.alloc.clone()); + + let initialized_clone = unsafe { + // Clone. If the clone panics, `in_progress` will be dropped and clean up. + this_data_ref.clone_to_uninit(in_progress.data_ptr()); + // Cast type of pointer, now that it is initialized. + in_progress.into_arc() + }; + *this = initialized_clone; } else if this.inner().weak.load(Relaxed) != 1 { // Relaxed suffices in the above because this is fundamentally an // optimization: we are always racing with weak pointers being @@ -2239,11 +2246,22 @@ impl Arc { let _weak = Weak { ptr: this.ptr, alloc: this.alloc.clone() }; // Can just steal the data, all that's left is Weaks - let mut arc = Self::new_uninit_in(this.alloc.clone()); + // + // We don't need panic-protection like the above branch does, but we might as well + // use the same mechanism. + let mut in_progress: UniqueArcUninit = + UniqueArcUninit::new(&**this, this.alloc.clone()); unsafe { - let data = Arc::get_mut_unchecked(&mut arc); - data.as_mut_ptr().copy_from_nonoverlapping(&**this, 1); - ptr::write(this, arc.assume_init()); + // Initialize `in_progress` with move of **this. + // We have to express this in terms of bytes because `T: ?Sized`; there is no + // operation that just copies a value based on its `size_of_val()`. + ptr::copy_nonoverlapping( + ptr::from_ref(&**this).cast::(), + in_progress.data_ptr().cast::(), + size_of_val, + ); + + ptr::write(this, in_progress.into_arc()); } } else { // We were the sole reference of either kind; bump back up the @@ -2511,7 +2529,7 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Arc { } impl Arc { - /// Attempt to downcast the `Arc` to a concrete type. + /// Attempts to downcast the `Arc` to a concrete type. /// /// # Examples /// @@ -2699,6 +2717,13 @@ impl Weak { } impl Weak { + /// Returns a reference to the underlying allocator. + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn allocator(&self) -> &A { + &self.alloc + } + /// Returns a raw pointer to the object `T` pointed to by this `Weak`. /// /// The pointer is valid only if there are some strong references. The pointer may be dangling, @@ -2771,9 +2796,7 @@ impl Weak { #[must_use = "losing the pointer will leak memory"] #[stable(feature = "weak_into_raw", since = "1.45.0")] pub fn into_raw(self) -> *const T { - let result = self.as_ptr(); - mem::forget(self); - result + ManuallyDrop::new(self).as_ptr() } /// Consumes the `Weak`, returning the wrapped pointer and allocator. @@ -3407,7 +3430,7 @@ static STATIC_INNER_SLICE: SliceArcInnerForStatic = SliceArcInnerForStatic { }; #[cfg(not(no_global_oom_handling))] -#[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] impl Default for Arc { /// Creates an empty str inside an Arc /// @@ -3422,7 +3445,7 @@ impl Default for Arc { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] impl Default for Arc { /// Creates an empty CStr inside an Arc /// @@ -3441,7 +3464,7 @@ impl Default for Arc { } #[cfg(not(no_global_oom_handling))] -#[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "more_rc_default_impls", since = "1.80.0")] impl Default for Arc<[T]> { /// Creates an empty `[T]` inside an Arc /// @@ -3520,7 +3543,7 @@ impl From<[T; N]> for Arc<[T]> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "shared_from_slice", since = "1.21.0")] impl From<&[T]> for Arc<[T]> { - /// Allocate a reference-counted slice and fill it by cloning `v`'s items. + /// Allocates a reference-counted slice and fills it by cloning `v`'s items. /// /// # Example /// @@ -3539,7 +3562,7 @@ impl From<&[T]> for Arc<[T]> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "shared_from_slice", since = "1.21.0")] impl From<&str> for Arc { - /// Allocate a reference-counted `str` and copy `v` into it. + /// Allocates a reference-counted `str` and copies `v` into it. /// /// # Example /// @@ -3558,7 +3581,7 @@ impl From<&str> for Arc { #[cfg(not(no_global_oom_handling))] #[stable(feature = "shared_from_slice", since = "1.21.0")] impl From for Arc { - /// Allocate a reference-counted `str` and copy `v` into it. + /// Allocates a reference-counted `str` and copies `v` into it. /// /// # Example /// @@ -3596,7 +3619,7 @@ impl From> for Arc { #[cfg(not(no_global_oom_handling))] #[stable(feature = "shared_from_slice", since = "1.21.0")] impl From> for Arc<[T], A> { - /// Allocate a reference-counted slice and move `v`'s items into it. + /// Allocates a reference-counted slice and moves `v`'s items into it. /// /// # Example /// @@ -3629,8 +3652,8 @@ where B: ToOwned + ?Sized, Arc: From<&'a B> + From, { - /// Create an atomically reference-counted pointer from - /// a clone-on-write pointer by copying its content. + /// Creates an atomically reference-counted pointer from a clone-on-write + /// pointer by copying its content. /// /// # Example /// @@ -3786,7 +3809,7 @@ impl AsRef for Arc { #[stable(feature = "pin", since = "1.33.0")] impl Unpin for Arc {} -/// Get the offset within an `ArcInner` for the payload behind a pointer. +/// Gets the offset within an `ArcInner` for the payload behind a pointer. /// /// # Safety /// @@ -3808,6 +3831,69 @@ fn data_offset_align(align: usize) -> usize { layout.size() + layout.padding_needed_for(align) } +/// A unique owning pointer to an [`ArcInner`] **that does not imply the contents are initialized,** +/// but will deallocate it (without dropping the value) when dropped. +/// +/// This is a helper for [`Arc::make_mut()`] to ensure correct cleanup on panic. +#[cfg(not(no_global_oom_handling))] +struct UniqueArcUninit { + ptr: NonNull>, + layout_for_value: Layout, + alloc: Option, +} + +#[cfg(not(no_global_oom_handling))] +impl UniqueArcUninit { + /// Allocates an ArcInner with layout suitable to contain `for_value` or a clone of it. + fn new(for_value: &T, alloc: A) -> UniqueArcUninit { + let layout = Layout::for_value(for_value); + let ptr = unsafe { + Arc::allocate_for_layout( + layout, + |layout_for_arcinner| alloc.allocate(layout_for_arcinner), + |mem| mem.with_metadata_of(ptr::from_ref(for_value) as *const ArcInner), + ) + }; + Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) } + } + + /// Returns the pointer to be written into to initialize the [`Arc`]. + fn data_ptr(&mut self) -> *mut T { + let offset = data_offset_align(self.layout_for_value.align()); + unsafe { self.ptr.as_ptr().byte_add(offset) as *mut T } + } + + /// Upgrade this into a normal [`Arc`]. + /// + /// # Safety + /// + /// The data must have been initialized (by writing to [`Self::data_ptr()`]). + unsafe fn into_arc(self) -> Arc { + let mut this = ManuallyDrop::new(self); + let ptr = this.ptr.as_ptr(); + let alloc = this.alloc.take().unwrap(); + + // SAFETY: The pointer is valid as per `UniqueArcUninit::new`, and the caller is responsible + // for having initialized the data. + unsafe { Arc::from_ptr_in(ptr, alloc) } + } +} + +#[cfg(not(no_global_oom_handling))] +impl Drop for UniqueArcUninit { + fn drop(&mut self) { + // SAFETY: + // * new() produced a pointer safe to deallocate. + // * We own the pointer unless into_arc() was called, which forgets us. + unsafe { + self.alloc.take().unwrap().deallocate( + self.ptr.cast(), + arcinner_layout_for_value_layout(self.layout_for_value), + ); + } + } +} + #[stable(feature = "arc_error", since = "1.52.0")] impl core::error::Error for Arc { #[allow(deprecated, deprecated_in_future)] diff --git a/library/alloc/src/sync/tests.rs b/library/alloc/src/sync/tests.rs index 49eae718c1690..d6b3de875771e 100644 --- a/library/alloc/src/sync/tests.rs +++ b/library/alloc/src/sync/tests.rs @@ -1,5 +1,3 @@ -use super::*; - use std::clone::Clone; use std::mem::MaybeUninit; use std::option::Option::None; @@ -9,6 +7,8 @@ use std::sync::mpsc::channel; use std::sync::Mutex; use std::thread; +use super::*; + struct Canary(*mut AtomicUsize); impl Drop for Canary { @@ -396,7 +396,7 @@ fn show_arc() { // Make sure deriving works with Arc #[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Debug, Default)] -struct Foo { +struct _Foo { inner: Arc, } diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index a3fa6585a37f8..27589aed2f97c 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -7,14 +7,14 @@ //! This may be detected at compile time using //! `#[cfg(target_has_atomic = "ptr")]`. -use crate::rc::Rc; use core::mem::ManuallyDrop; +#[cfg(target_has_atomic = "ptr")] +use core::task::Waker; use core::task::{LocalWaker, RawWaker, RawWakerVTable}; +use crate::rc::Rc; #[cfg(target_has_atomic = "ptr")] use crate::sync::Arc; -#[cfg(target_has_atomic = "ptr")] -use core::task::Waker; /// The implementation of waking a task on an executor. /// diff --git a/library/alloc/src/testing/crash_test.rs b/library/alloc/src/testing/crash_test.rs index bcf5f5f72510e..684bac60d9a86 100644 --- a/library/alloc/src/testing/crash_test.rs +++ b/library/alloc/src/testing/crash_test.rs @@ -1,7 +1,8 @@ -// We avoid relying on anything else in the crate, apart from the `Debug` trait. -use crate::fmt::Debug; use std::cmp::Ordering; -use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; + +use crate::fmt::Debug; // the `Debug` trait is the only thing we use from `crate::fmt` /// A blueprint for crash test dummy instances that monitor particular events. /// Some instances may be configured to panic at some point. diff --git a/library/alloc/src/tests.rs b/library/alloc/src/tests.rs index ab256ceaec353..b95d11cb07ec8 100644 --- a/library/alloc/src/tests.rs +++ b/library/alloc/src/tests.rs @@ -2,7 +2,6 @@ use core::any::Any; use core::ops::Deref; - use std::boxed::Box; #[test] diff --git a/library/alloc/src/vec/cow.rs b/library/alloc/src/vec/cow.rs index 3fe83242a30ad..c18091705a636 100644 --- a/library/alloc/src/vec/cow.rs +++ b/library/alloc/src/vec/cow.rs @@ -1,6 +1,5 @@ -use crate::borrow::Cow; - use super::Vec; +use crate::borrow::Cow; #[stable(feature = "cow_from_vec", since = "1.8.0")] impl<'a, T: Clone> From<&'a [T]> for Cow<'a, [T]> { diff --git a/library/alloc/src/vec/drain.rs b/library/alloc/src/vec/drain.rs index f0b63759ac70f..9362cef2a1b00 100644 --- a/library/alloc/src/vec/drain.rs +++ b/library/alloc/src/vec/drain.rs @@ -1,4 +1,3 @@ -use crate::alloc::{Allocator, Global}; use core::fmt; use core::iter::{FusedIterator, TrustedLen}; use core::mem::{self, ManuallyDrop, SizedTypeProperties}; @@ -6,6 +5,7 @@ use core::ptr::{self, NonNull}; use core::slice::{self}; use super::Vec; +use crate::alloc::{Allocator, Global}; /// A draining iterator for `Vec`. /// diff --git a/library/alloc/src/vec/extract_if.rs b/library/alloc/src/vec/extract_if.rs index 118cfdb36b9c2..72d51e8904488 100644 --- a/library/alloc/src/vec/extract_if.rs +++ b/library/alloc/src/vec/extract_if.rs @@ -1,8 +1,7 @@ -use crate::alloc::{Allocator, Global}; -use core::ptr; -use core::slice; +use core::{ptr, slice}; use super::Vec; +use crate::alloc::{Allocator, Global}; /// An iterator which uses a closure to determine if an element should be removed. /// diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index 22541a2b9d82f..d119e6ca397c5 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -154,9 +154,8 @@ //! } //! vec.truncate(write_idx); //! ``` -use crate::alloc::{handle_alloc_error, Global}; -use core::alloc::Allocator; -use core::alloc::Layout; + +use core::alloc::{Allocator, Layout}; use core::iter::{InPlaceIterable, SourceIter, TrustedRandomAccessNoCoerce}; use core::marker::PhantomData; use core::mem::{self, ManuallyDrop, SizedTypeProperties}; @@ -164,6 +163,7 @@ use core::num::NonZero; use core::ptr; use super::{InPlaceDrop, InPlaceDstDataSrcBufDrop, SpecFromIter, SpecFromIterNested, Vec}; +use crate::alloc::{handle_alloc_error, Global}; const fn in_place_collectible( step_merge: Option>, diff --git a/library/alloc/src/vec/in_place_drop.rs b/library/alloc/src/vec/in_place_drop.rs index 4050c250130bb..27f7597931045 100644 --- a/library/alloc/src/vec/in_place_drop.rs +++ b/library/alloc/src/vec/in_place_drop.rs @@ -1,6 +1,5 @@ use core::marker::PhantomData; -use core::ptr::NonNull; -use core::ptr::{self, drop_in_place}; +use core::ptr::{self, drop_in_place, NonNull}; use core::slice::{self}; use crate::alloc::Global; diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index c47989337708f..fad8abad49353 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -1,11 +1,3 @@ -#[cfg(not(no_global_oom_handling))] -use super::AsVecIntoIter; -use crate::alloc::{Allocator, Global}; -#[cfg(not(no_global_oom_handling))] -use crate::collections::VecDeque; -use crate::raw_vec::RawVec; -use core::array; -use core::fmt; use core::iter::{ FusedIterator, InPlaceIterable, SourceIter, TrustedFused, TrustedLen, TrustedRandomAccessNoCoerce, @@ -17,6 +9,14 @@ use core::num::NonZero; use core::ops::Deref; use core::ptr::{self, NonNull}; use core::slice::{self}; +use core::{array, fmt}; + +#[cfg(not(no_global_oom_handling))] +use super::AsVecIntoIter; +use crate::alloc::{Allocator, Global}; +#[cfg(not(no_global_oom_handling))] +use crate::collections::VecDeque; +use crate::raw_vec::RawVec; macro non_null { (mut $place:expr, $t:ident) => {{ @@ -114,16 +114,22 @@ impl IntoIter { } /// Drops remaining elements and relinquishes the backing allocation. - /// This method guarantees it won't panic before relinquishing - /// the backing allocation. + /// + /// This method guarantees it won't panic before relinquishing the backing + /// allocation. /// /// This is roughly equivalent to the following, but more efficient /// /// ``` - /// # let mut into_iter = Vec::::with_capacity(10).into_iter(); + /// # let mut vec = Vec::::with_capacity(10); + /// # let ptr = vec.as_mut_ptr(); + /// # let mut into_iter = vec.into_iter(); /// let mut into_iter = std::mem::replace(&mut into_iter, Vec::new().into_iter()); /// (&mut into_iter).for_each(drop); /// std::mem::forget(into_iter); + /// # // FIXME(https://github.com/rust-lang/miri/issues/3670): + /// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak. + /// # drop(unsafe { Vec::::from_raw_parts(ptr, 0, 10) }); /// ``` /// /// This method is used by in-place iteration, refer to the vec::in_place_collect @@ -254,7 +260,7 @@ impl Iterator for IntoIter { #[inline] fn next_chunk(&mut self) -> Result<[T; N], core::array::IntoIter> { - let mut raw_ary = MaybeUninit::uninit_array(); + let mut raw_ary = [const { MaybeUninit::uninit() }; N]; let len = self.len(); diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index b2e22d8715a8b..b4e0bc5fcbe41 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -66,15 +66,14 @@ use core::ops::{self, Index, IndexMut, Range, RangeBounds}; use core::ptr::{self, NonNull}; use core::slice::{self, SliceIndex}; +#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] +pub use self::extract_if::ExtractIf; use crate::alloc::{Allocator, Global}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::collections::TryReserveError; use crate::raw_vec::RawVec; -#[unstable(feature = "extract_if", reason = "recently added", issue = "43244")] -pub use self::extract_if::ExtractIf; - mod extract_if; #[cfg(not(no_global_oom_handling))] @@ -879,6 +878,7 @@ impl Vec { /// }; /// assert_eq!(rebuilt, [4294967295, 0, 1]); /// ``` + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_raw_parts(self) -> (*mut T, usize, usize) { let mut me = ManuallyDrop::new(self); @@ -922,6 +922,7 @@ impl Vec { /// }; /// assert_eq!(rebuilt, [4294967295, 0, 1]); /// ``` + #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] // #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) { @@ -1101,6 +1102,7 @@ impl Vec { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn shrink_to_fit(&mut self) { // The capacity is never less than the length, and there's nothing to do when // they are equal, so we can avoid the panic case in `RawVec::shrink_to_fit` @@ -1276,7 +1278,7 @@ impl Vec { /// valid for zero sized reads if the vector didn't allocate. /// /// The caller must ensure that the vector outlives the pointer this - /// function returns, or else it will end up pointing to garbage. + /// function returns, or else it will end up dangling. /// Modifying the vector may cause its buffer to be reallocated, /// which would also make any pointers to it invalid. /// @@ -1336,7 +1338,7 @@ impl Vec { /// raw pointer valid for zero sized reads if the vector didn't allocate. /// /// The caller must ensure that the vector outlives the pointer this - /// function returns, or else it will end up pointing to garbage. + /// function returns, or else it will end up dangling. /// Modifying the vector may cause its buffer to be reallocated, /// which would also make any pointers to it invalid. /// @@ -1472,6 +1474,9 @@ impl Vec { /// // 2. `0 <= capacity` always holds whatever `capacity` is. /// unsafe { /// vec.set_len(0); + /// # // FIXME(https://github.com/rust-lang/miri/issues/3670): + /// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak. + /// # vec.set_len(3); /// } /// ``` /// @@ -1707,6 +1712,12 @@ impl Vec { F: FnMut(&mut T) -> bool, { let original_len = self.len(); + + if original_len == 0 { + // Empty case: explicit return allows better optimization, vs letting compiler infer it + return; + } + // Avoid double drop if the drop guard is not executed, // since we may make some holes during the process. unsafe { self.set_len(0) }; @@ -2369,9 +2380,11 @@ impl Vec { } /// Consumes and leaks the `Vec`, returning a mutable reference to the contents, - /// `&'a mut [T]`. Note that the type `T` must outlive the chosen lifetime - /// `'a`. If the type has only static references, or none at all, then this - /// may be chosen to be `'static`. + /// `&'a mut [T]`. + /// + /// Note that the type `T` must outlive the chosen lifetime `'a`. If the type + /// has only static references, or none at all, then this may be chosen to be + /// `'static`. /// /// As of Rust 1.57, this method does not reallocate or shrink the `Vec`, /// so the leaked allocation may include unused capacity that is not part @@ -2390,6 +2403,9 @@ impl Vec { /// let static_ref: &'static mut [usize] = x.leak(); /// static_ref[0] += 1; /// assert_eq!(static_ref, &[2, 2, 3]); + /// # // FIXME(https://github.com/rust-lang/miri/issues/3670): + /// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak. + /// # drop(unsafe { Box::from_raw(static_ref) }); /// ``` #[stable(feature = "vec_leak", since = "1.47.0")] #[inline] @@ -2643,15 +2659,13 @@ impl Vec<[T; N], A> { /// # Examples /// /// ``` - /// #![feature(slice_flatten)] - /// /// let mut vec = vec![[1, 2, 3], [4, 5, 6], [7, 8, 9]]; /// assert_eq!(vec.pop(), Some([7, 8, 9])); /// /// let mut flattened = vec.into_flattened(); /// assert_eq!(flattened.pop(), Some(6)); /// ``` - #[unstable(feature = "slice_flatten", issue = "95629")] + #[stable(feature = "slice_flatten", since = "1.80.0")] pub fn into_flattened(self) -> Vec { let (ptr, len, cap, alloc) = self.into_raw_parts_with_alloc(); let (new_len, new_cap) = if T::IS_ZST { @@ -3049,6 +3063,16 @@ impl Extend for Vec { fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } + + #[inline] + unsafe fn extend_one_unchecked(&mut self, item: T) { + // SAFETY: Our preconditions ensure the space has been reserved, and `extend_reserve` is implemented correctly. + unsafe { + let len = self.len(); + ptr::write(self.as_mut_ptr().add(len), item); + self.set_len(len + 1); + } + } } impl Vec { @@ -3245,6 +3269,16 @@ impl<'a, T: Copy + 'a, A: Allocator> Extend<&'a T> for Vec { fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } + + #[inline] + unsafe fn extend_one_unchecked(&mut self, &item: &'a T) { + // SAFETY: Our preconditions ensure the space has been reserved, and `extend_reserve` is implemented correctly. + unsafe { + let len = self.len(); + ptr::write(self.as_mut_ptr().add(len), item); + self.set_len(len + 1); + } + } } /// Implements comparison of vectors, [lexicographically](Ord#lexicographical-comparison). @@ -3334,7 +3368,7 @@ impl AsMut<[T]> for Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl From<&[T]> for Vec { - /// Allocate a `Vec` and fill it by cloning `s`'s items. + /// Allocates a `Vec` and fills it by cloning `s`'s items. /// /// # Examples /// @@ -3354,7 +3388,7 @@ impl From<&[T]> for Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_from_mut", since = "1.19.0")] impl From<&mut [T]> for Vec { - /// Allocate a `Vec` and fill it by cloning `s`'s items. + /// Allocates a `Vec` and fills it by cloning `s`'s items. /// /// # Examples /// @@ -3374,7 +3408,7 @@ impl From<&mut [T]> for Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_from_array_ref", since = "1.74.0")] impl From<&[T; N]> for Vec { - /// Allocate a `Vec` and fill it by cloning `s`'s items. + /// Allocates a `Vec` and fills it by cloning `s`'s items. /// /// # Examples /// @@ -3389,7 +3423,7 @@ impl From<&[T; N]> for Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_from_array_ref", since = "1.74.0")] impl From<&mut [T; N]> for Vec { - /// Allocate a `Vec` and fill it by cloning `s`'s items. + /// Allocates a `Vec` and fills it by cloning `s`'s items. /// /// # Examples /// @@ -3404,7 +3438,7 @@ impl From<&mut [T; N]> for Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_from_array", since = "1.44.0")] impl From<[T; N]> for Vec { - /// Allocate a `Vec` and move `s`'s items into it. + /// Allocates a `Vec` and moves `s`'s items into it. /// /// # Examples /// @@ -3427,7 +3461,7 @@ impl<'a, T> From> for Vec where [T]: ToOwned>, { - /// Convert a clone-on-write slice into a vector. + /// Converts a clone-on-write slice into a vector. /// /// If `s` already owns a `Vec`, it will be returned directly. /// If `s` is borrowing a slice, a new `Vec` will be allocated and @@ -3450,7 +3484,7 @@ where #[cfg(not(test))] #[stable(feature = "vec_from_box", since = "1.18.0")] impl From> for Vec { - /// Convert a boxed slice into a vector by transferring ownership of + /// Converts a boxed slice into a vector by transferring ownership of /// the existing heap allocation. /// /// # Examples @@ -3469,7 +3503,7 @@ impl From> for Vec { #[cfg(not(test))] #[stable(feature = "box_from_vec", since = "1.20.0")] impl From> for Box<[T], A> { - /// Convert a vector into a boxed slice. + /// Converts a vector into a boxed slice. /// /// Before doing the conversion, this method discards excess capacity like [`Vec::shrink_to_fit`]. /// @@ -3497,7 +3531,7 @@ impl From> for Box<[T], A> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl From<&str> for Vec { - /// Allocate a `Vec` and fill it with a UTF-8 string. + /// Allocates a `Vec` and fills it with a UTF-8 string. /// /// # Examples /// diff --git a/library/alloc/src/vec/partial_eq.rs b/library/alloc/src/vec/partial_eq.rs index b0cf72577a1be..5e620c4b2efe7 100644 --- a/library/alloc/src/vec/partial_eq.rs +++ b/library/alloc/src/vec/partial_eq.rs @@ -1,9 +1,8 @@ +use super::Vec; use crate::alloc::Allocator; #[cfg(not(no_global_oom_handling))] use crate::borrow::Cow; -use super::Vec; - macro_rules! __impl_slice_eq1 { ([$($vars:tt)*] $lhs:ty, $rhs:ty $(where $ty:ty: $bound:ident)?, #[$stability:meta]) => { #[$stability] diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs index e2f865d0f7167..7085bceef5baa 100644 --- a/library/alloc/src/vec/spec_extend.rs +++ b/library/alloc/src/vec/spec_extend.rs @@ -1,8 +1,8 @@ -use crate::alloc::Allocator; use core::iter::TrustedLen; use core::slice::{self}; use super::{IntoIter, Vec}; +use crate::alloc::Allocator; // Specialization trait used for Vec::extend pub(super) trait SpecExtend { diff --git a/library/alloc/src/vec/spec_from_elem.rs b/library/alloc/src/vec/spec_from_elem.rs index 01a6db14474bb..96d701e15d487 100644 --- a/library/alloc/src/vec/spec_from_elem.rs +++ b/library/alloc/src/vec/spec_from_elem.rs @@ -1,10 +1,9 @@ use core::ptr; +use super::{IsZero, Vec}; use crate::alloc::Allocator; use crate::raw_vec::RawVec; -use super::{IsZero, Vec}; - // Specialization trait used for Vec::from_elem pub(super) trait SpecFromElem: Sized { fn from_elem(elem: Self, n: usize, alloc: A) -> Vec; diff --git a/library/alloc/src/vec/spec_from_iter_nested.rs b/library/alloc/src/vec/spec_from_iter_nested.rs index f915ebb86e5a5..77f7761d22f95 100644 --- a/library/alloc/src/vec/spec_from_iter_nested.rs +++ b/library/alloc/src/vec/spec_from_iter_nested.rs @@ -1,10 +1,8 @@ -use core::cmp; use core::iter::TrustedLen; -use core::ptr; - -use crate::raw_vec::RawVec; +use core::{cmp, ptr}; use super::{SpecExtend, Vec}; +use crate::raw_vec::RawVec; /// Another specialization trait for Vec::from_iter /// necessary to manually prioritize overlapping specializations diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs index 852fdcc3f5ce7..9e36377c148d2 100644 --- a/library/alloc/src/vec/splice.rs +++ b/library/alloc/src/vec/splice.rs @@ -1,8 +1,8 @@ -use crate::alloc::{Allocator, Global}; use core::ptr::{self}; use core::slice::{self}; use super::{Drain, Vec}; +use crate::alloc::{Allocator, Global}; /// A splicing iterator for `Vec`. /// diff --git a/library/alloc/tests/arc.rs b/library/alloc/tests/arc.rs index d564a30b10394..c37a80dca95c8 100644 --- a/library/alloc/tests/arc.rs +++ b/library/alloc/tests/arc.rs @@ -209,3 +209,21 @@ fn weak_may_dangle() { // `val` dropped here while still borrowed // borrow might be used here, when `val` is dropped and runs the `Drop` code for type `std::sync::Weak` } + +/// This is similar to the doc-test for `Arc::make_mut()`, but on an unsized type (slice). +#[test] +fn make_mut_unsized() { + use alloc::sync::Arc; + + let mut data: Arc<[i32]> = Arc::new([10, 20, 30]); + + Arc::make_mut(&mut data)[0] += 1; // Won't clone anything + let mut other_data = Arc::clone(&data); // Won't clone inner data + Arc::make_mut(&mut data)[1] += 1; // Clones inner data + Arc::make_mut(&mut data)[2] += 1; // Won't clone anything + Arc::make_mut(&mut other_data)[0] *= 10; // Won't clone anything + + // Now `data` and `other_data` point to different allocations. + assert_eq!(*data, [11, 21, 31]); + assert_eq!(*other_data, [110, 20, 30]); +} diff --git a/library/alloc/tests/btree_set_hash.rs b/library/alloc/tests/btree_set_hash.rs index ab275ac4353ac..71a3a143209ff 100644 --- a/library/alloc/tests/btree_set_hash.rs +++ b/library/alloc/tests/btree_set_hash.rs @@ -1,6 +1,7 @@ -use crate::hash; use std::collections::BTreeSet; +use crate::hash; + #[test] fn test_hash() { let mut x = BTreeSet::new(); diff --git a/library/alloc/tests/fmt.rs b/library/alloc/tests/fmt.rs index 379e09ab69a3c..ce24a40f4c051 100644 --- a/library/alloc/tests/fmt.rs +++ b/library/alloc/tests/fmt.rs @@ -217,19 +217,19 @@ fn test_format_macro_interface() { // make sure that format! doesn't move out of local variables let a = Box::new(3); - format!("{a}"); - format!("{a}"); + let _ = format!("{a}"); + let _ = format!("{a}"); // make sure that format! doesn't cause spurious unused-unsafe warnings when // it's inside of an outer unsafe block unsafe { let a: isize = ::std::mem::transmute(3_usize); - format!("{a}"); + let _ = format!("{a}"); } // test that trailing commas are acceptable - format!("{}", "test",); - format!("{foo}", foo = "test",); + let _ = format!("{}", "test",); + let _ = format!("{foo}", foo = "test",); } // Basic test to make sure that we can invoke the `write!` macro with an diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 0eae4ca4b8ba3..89538f272f069 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -24,7 +24,6 @@ #![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_drain_sorted)] #![feature(slice_ptr_get)] -#![feature(binary_heap_as_slice)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] @@ -36,7 +35,6 @@ #![feature(const_str_from_utf8)] #![feature(panic_update_hook)] #![feature(pointer_is_aligned_to)] -#![feature(slice_flatten)] #![feature(thin_box)] #![feature(strict_provenance)] #![feature(drain_keep_rest)] diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs index c0f7a11a93e12..df5a8af16fd70 100644 --- a/library/alloc/tests/slice.rs +++ b/library/alloc/tests/slice.rs @@ -1,9 +1,7 @@ use std::cmp::Ordering::{Equal, Greater, Less}; use std::convert::identity; -use std::fmt; -use std::mem; -use std::panic; use std::rc::Rc; +use std::{fmt, mem, panic}; fn square(n: usize) -> usize { n * n @@ -911,8 +909,7 @@ fn test_split_iterators_size_hint() { // become maximally long, so the size_hint upper bounds are tight ((|_| true) as fn(&_) -> _, Bounds::Upper), ] { - use assert_tight_size_hints as a; - use format_args as f; + use {assert_tight_size_hints as a, format_args as f}; a(v.split(p), b, "split"); a(v.split_mut(p), b, "split_mut"); diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index 0078f5eaa3d2b..a6b1fe5b97945 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -165,7 +165,8 @@ fn test_join_for_different_lengths_with_long_separator() { #[test] fn test_join_issue_80335() { - use core::{borrow::Borrow, cell::Cell}; + use core::borrow::Borrow; + use core::cell::Cell; struct WeirdBorrow { state: Cell, @@ -1927,12 +1928,10 @@ mod pattern { } } - fn cmp_search_to_vec<'a>( - rev: bool, - pat: impl Pattern<'a, Searcher: ReverseSearcher<'a>>, - haystack: &'a str, - right: Vec, - ) { + fn cmp_search_to_vec

(rev: bool, pat: P, haystack: &str, right: Vec) + where + P: for<'a> Pattern: ReverseSearcher<'a>>, + { let mut searcher = pat.into_searcher(haystack); let mut v = vec![]; loop { @@ -2191,9 +2190,9 @@ generate_iterator_test! { fn different_str_pattern_forwarding_lifetimes() { use std::str::pattern::Pattern; - fn foo<'a, P>(p: P) + fn foo

(p: P) where - for<'b> &'b P: Pattern<'a>, + for<'b> &'b P: Pattern, { for _ in 0..3 { "asdf".find(&p); diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index e20ceae87b0d5..c5bc4185a3670 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -2,11 +2,9 @@ use std::assert_matches::assert_matches; use std::borrow::Cow; use std::cell::Cell; use std::collections::TryReserveErrorKind::*; -use std::ops::Bound; use std::ops::Bound::*; -use std::ops::RangeBounds; -use std::panic; -use std::str; +use std::ops::{Bound, RangeBounds}; +use std::{panic, str}; pub trait IntoCow<'a, B: ?Sized> where diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 71d79893e01d7..fd2ddbf59e42d 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -8,15 +8,14 @@ use std::borrow::Cow; use std::cell::Cell; use std::collections::TryReserveErrorKind::*; use std::fmt::Debug; -use std::hint; use std::iter::InPlaceIterable; -use std::mem; use std::mem::{size_of, swap}; use std::ops::Bound::*; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::rc::Rc; use std::sync::atomic::{AtomicU32, Ordering}; use std::vec::{Drain, IntoIter}; +use std::{hint, mem}; struct DropCounter<'a> { count: &'a mut u32, @@ -2572,7 +2571,8 @@ fn test_into_flattened_size_overflow() { #[test] fn test_box_zero_allocator() { - use core::{alloc::AllocError, cell::RefCell}; + use core::alloc::AllocError; + use core::cell::RefCell; use std::collections::HashSet; // Track ZST allocations and ensure that they all have a matching free. diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index cea5de4dd5984..db972122fef2a 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -1,16 +1,17 @@ use core::num::NonZero; use std::assert_matches::assert_matches; +use std::collections::vec_deque::Drain; use std::collections::TryReserveErrorKind::*; -use std::collections::{vec_deque::Drain, VecDeque}; +use std::collections::VecDeque; use std::fmt::Debug; use std::ops::Bound::*; use std::panic::{catch_unwind, AssertUnwindSafe}; -use crate::hash; - use Taggy::*; use Taggypar::*; +use crate::hash; + #[test] fn test_simple() { let mut d = VecDeque::new(); diff --git a/library/alloc/tests/vec_deque_alloc_error.rs b/library/alloc/tests/vec_deque_alloc_error.rs new file mode 100644 index 0000000000000..c41d8266eb457 --- /dev/null +++ b/library/alloc/tests/vec_deque_alloc_error.rs @@ -0,0 +1,48 @@ +#![feature(alloc_error_hook, allocator_api)] + +use std::alloc::{set_alloc_error_hook, AllocError, Allocator, Layout, System}; +use std::collections::VecDeque; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::ptr::NonNull; + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_shrink_to_unwind() { + // This tests that `shrink_to` leaves the deque in a consistent state when + // the call to `RawVec::shrink_to_fit` unwinds. The code is adapted from #123369 + // but changed to hopefully not have any UB even if the test fails. + + struct BadAlloc; + + unsafe impl Allocator for BadAlloc { + fn allocate(&self, l: Layout) -> Result, AllocError> { + // We allocate zeroed here so that the whole buffer of the deque + // is always initialized. That way, even if the deque is left in + // an inconsistent state, no uninitialized memory should be accessed. + System.allocate_zeroed(l) + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + unsafe { System.deallocate(ptr, layout) } + } + + unsafe fn shrink( + &self, + _ptr: NonNull, + _old_layout: Layout, + _new_layout: Layout, + ) -> Result, AllocError> { + Err(AllocError) + } + } + + set_alloc_error_hook(|_| panic!("alloc error")); + + let mut v = VecDeque::with_capacity_in(15, BadAlloc); + v.push_back(1); + v.push_front(2); + // This should unwind because it calls `BadAlloc::shrink` and then `handle_alloc_error` which unwinds. + assert!(catch_unwind(AssertUnwindSafe(|| v.shrink_to_fit())).is_err()); + // This should only pass if the deque is left in a consistent state. + assert_eq!(v, [2, 1]); +} diff --git a/library/backtrace b/library/backtrace index e15130618237e..fd0aed536dd9d 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit e15130618237eb3e2d4b622549f9647b4c1d9ca3 +Subproject commit fd0aed536dd9d6ad1511a4ab3c350409f36ee52a diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index e9fb39f19966c..65c3a57ae7116 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -42,9 +42,6 @@ debug_refcell = [] [lints.rust.unexpected_cfgs] level = "warn" -# x.py uses beta cargo, so `check-cfg` entries do not yet take effect -# for rust-lang/rust. But for users of `-Zbuild-std` it does. -# The unused warning is waiting for rust-lang/cargo#13925 to reach beta. check-cfg = [ 'cfg(bootstrap)', 'cfg(no_fp_fmt_parse)', diff --git a/library/core/benches/any.rs b/library/core/benches/any.rs index 53099b78266f8..f6be41bcdbfa7 100644 --- a/library/core/benches/any.rs +++ b/library/core/benches/any.rs @@ -1,4 +1,5 @@ use core::any::*; + use test::{black_box, Bencher}; #[bench] diff --git a/library/core/benches/array.rs b/library/core/benches/array.rs index d8cc44d05c4ba..b1a41a088c493 100644 --- a/library/core/benches/array.rs +++ b/library/core/benches/array.rs @@ -1,5 +1,4 @@ -use test::black_box; -use test::Bencher; +use test::{black_box, Bencher}; macro_rules! map_array { ($func_name:ident, $start_item: expr, $map_item: expr, $arr_size: expr) => { diff --git a/library/core/benches/ascii.rs b/library/core/benches/ascii.rs index 71ec9fed2fe75..61bf8bbf411d5 100644 --- a/library/core/benches/ascii.rs +++ b/library/core/benches/ascii.rs @@ -64,8 +64,8 @@ macro_rules! benches { } use std::fmt::Write; -use test::black_box; -use test::Bencher; + +use test::{black_box, Bencher}; const ASCII_CASE_MASK: u8 = 0b0010_0000; diff --git a/library/core/benches/ascii/is_ascii.rs b/library/core/benches/ascii/is_ascii.rs index a42a1dcfe3988..05f60a46f8be2 100644 --- a/library/core/benches/ascii/is_ascii.rs +++ b/library/core/benches/ascii/is_ascii.rs @@ -1,6 +1,6 @@ +use test::{black_box, Bencher}; + use super::{LONG, MEDIUM, SHORT}; -use test::black_box; -use test::Bencher; macro_rules! benches { ($( fn $name: ident($arg: ident: &[u8]) $body: block )+) => { diff --git a/library/core/benches/fmt.rs b/library/core/benches/fmt.rs index d1cdb12e50f8c..906d7ac3eef28 100644 --- a/library/core/benches/fmt.rs +++ b/library/core/benches/fmt.rs @@ -1,5 +1,6 @@ use std::fmt::{self, Write as FmtWrite}; use std::io::{self, Write as IoWrite}; + use test::{black_box, Bencher}; #[bench] diff --git a/library/core/benches/hash/sip.rs b/library/core/benches/hash/sip.rs index 725c864dce9f1..8e8c07b6ee4c3 100644 --- a/library/core/benches/hash/sip.rs +++ b/library/core/benches/hash/sip.rs @@ -1,6 +1,7 @@ #![allow(deprecated)] use core::hash::*; + use test::{black_box, Bencher}; fn hash_bytes(mut s: H, x: &[u8]) -> u64 { diff --git a/library/core/benches/iter.rs b/library/core/benches/iter.rs index c1cec5e6d3c8c..24469ba0c4262 100644 --- a/library/core/benches/iter.rs +++ b/library/core/benches/iter.rs @@ -3,6 +3,7 @@ use core::iter::*; use core::mem; use core::num::Wrapping; use core::ops::Range; + use test::{black_box, Bencher}; #[bench] diff --git a/library/core/benches/num/flt2dec/mod.rs b/library/core/benches/num/flt2dec/mod.rs index b1a9fc56bae54..6c7de5dcd2286 100644 --- a/library/core/benches/num/flt2dec/mod.rs +++ b/library/core/benches/num/flt2dec/mod.rs @@ -3,9 +3,9 @@ mod strategy { mod grisu; } -use core::num::flt2dec::MAX_SIG_DIGITS; -use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded}; +use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded, MAX_SIG_DIGITS}; use std::io::Write; + use test::{black_box, Bencher}; pub fn decode_finite(v: T) -> Decoded { diff --git a/library/core/benches/num/flt2dec/strategy/dragon.rs b/library/core/benches/num/flt2dec/strategy/dragon.rs index babedc6c0ec80..4526697140365 100644 --- a/library/core/benches/num/flt2dec/strategy/dragon.rs +++ b/library/core/benches/num/flt2dec/strategy/dragon.rs @@ -1,7 +1,8 @@ -use super::super::*; use core::num::flt2dec::strategy::dragon::*; use std::mem::MaybeUninit; +use super::super::*; + #[bench] fn bench_small_shortest(b: &mut Bencher) { let decoded = decode_finite(3.141592f64); diff --git a/library/core/benches/num/flt2dec/strategy/grisu.rs b/library/core/benches/num/flt2dec/strategy/grisu.rs index b5bddb2c7c746..d20f9b02f7e74 100644 --- a/library/core/benches/num/flt2dec/strategy/grisu.rs +++ b/library/core/benches/num/flt2dec/strategy/grisu.rs @@ -1,7 +1,8 @@ -use super::super::*; use core::num::flt2dec::strategy::grisu::*; use std::mem::MaybeUninit; +use super::super::*; + pub fn decode_finite(v: T) -> Decoded { match decode(v).1 { FullDecoded::Finite(decoded) => decoded, diff --git a/library/core/benches/num/mod.rs b/library/core/benches/num/mod.rs index 4922ee150d95f..c1dc3a3062256 100644 --- a/library/core/benches/num/mod.rs +++ b/library/core/benches/num/mod.rs @@ -4,6 +4,7 @@ mod int_log; mod int_pow; use std::str::FromStr; + use test::{black_box, Bencher}; const ASCII_NUMBERS: [&str; 19] = [ diff --git a/library/core/benches/ops.rs b/library/core/benches/ops.rs index 0a2be8a28819f..3d0b3302957bf 100644 --- a/library/core/benches/ops.rs +++ b/library/core/benches/ops.rs @@ -1,4 +1,5 @@ use core::ops::*; + use test::Bencher; // Overhead of dtors diff --git a/library/core/benches/pattern.rs b/library/core/benches/pattern.rs index 480ac6f36d202..0d60b005feb32 100644 --- a/library/core/benches/pattern.rs +++ b/library/core/benches/pattern.rs @@ -1,5 +1,4 @@ -use test::black_box; -use test::Bencher; +use test::{black_box, Bencher}; #[bench] fn starts_with_char(b: &mut Bencher) { diff --git a/library/core/benches/slice.rs b/library/core/benches/slice.rs index 8f87a211449c0..2741dbd53f14c 100644 --- a/library/core/benches/slice.rs +++ b/library/core/benches/slice.rs @@ -1,6 +1,6 @@ use core::ptr::NonNull; -use test::black_box; -use test::Bencher; + +use test::{black_box, Bencher}; enum Cache { L1, diff --git a/library/core/benches/str.rs b/library/core/benches/str.rs index 0f14809444bc5..a8178f9c18752 100644 --- a/library/core/benches/str.rs +++ b/library/core/benches/str.rs @@ -1,4 +1,5 @@ use std::str; + use test::{black_box, Bencher}; mod char_count; diff --git a/library/core/benches/str/char_count.rs b/library/core/benches/str/char_count.rs index 25d9b2e299223..b87ad0f6adf24 100644 --- a/library/core/benches/str/char_count.rs +++ b/library/core/benches/str/char_count.rs @@ -1,6 +1,7 @@ -use super::corpora::*; use test::{black_box, Bencher}; +use super::corpora::*; + macro_rules! define_benches { ($( fn $name: ident($arg: ident: &str) $body: block )+) => { define_benches!(mod en_tiny, en::TINY, $($name $arg $body)+); diff --git a/library/core/benches/str/debug.rs b/library/core/benches/str/debug.rs index cb91169eed8eb..6dbf4e92c084b 100644 --- a/library/core/benches/str/debug.rs +++ b/library/core/benches/str/debug.rs @@ -4,6 +4,7 @@ //! we should still try to minimize those calls over time rather than regress them. use std::fmt::{self, Write}; + use test::{black_box, Bencher}; #[derive(Default)] diff --git a/library/core/benches/str/iter.rs b/library/core/benches/str/iter.rs index 58ae71fc10f12..f6e73e48d8e7d 100644 --- a/library/core/benches/str/iter.rs +++ b/library/core/benches/str/iter.rs @@ -1,6 +1,7 @@ -use super::corpora; use test::{black_box, Bencher}; +use super::corpora; + #[bench] fn chars_advance_by_1000(b: &mut Bencher) { b.iter(|| black_box(corpora::ru::LARGE).chars().advance_by(1000)); diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index 8df3ace54ffe1..a6f799c4a7deb 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -1,6 +1,5 @@ use crate::alloc::Layout; -use crate::cmp; -use crate::ptr; +use crate::{cmp, ptr}; /// A memory allocator that can be registered as the standard library’s default /// through the `#[global_allocator]` attribute. @@ -118,7 +117,7 @@ use crate::ptr; /// having side effects. #[stable(feature = "global_alloc", since = "1.28.0")] pub unsafe trait GlobalAlloc { - /// Allocate memory as described by the given `layout`. + /// Allocates memory as described by the given `layout`. /// /// Returns a pointer to newly-allocated memory, /// or null to indicate allocation failure. @@ -153,7 +152,7 @@ pub unsafe trait GlobalAlloc { #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn alloc(&self, layout: Layout) -> *mut u8; - /// Deallocate the block of memory at the given `ptr` pointer with the given `layout`. + /// Deallocates the block of memory at the given `ptr` pointer with the given `layout`. /// /// # Safety /// @@ -200,7 +199,7 @@ pub unsafe trait GlobalAlloc { ptr } - /// Shrink or grow a block of memory to the given `new_size` in bytes. + /// Shrinks or grows a block of memory to the given `new_size` in bytes. /// The block is described by the given `ptr` pointer and `layout`. /// /// If this returns a non-null pointer, then ownership of the memory block @@ -232,7 +231,7 @@ pub unsafe trait GlobalAlloc { /// * `new_size` must be greater than zero. /// /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, - /// must not overflow isize (i.e., the rounded value must be less than or + /// must not overflow `isize` (i.e., the rounded value must be less than or /// equal to `isize::MAX`). /// /// (Extension subtraits might provide more specific bounds on diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index c63db5aa0aa2f..4037ea1935832 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -5,11 +5,9 @@ // Your performance intuition is useless. Run perf. use safety::requires; -use crate::cmp; use crate::error::Error; -use crate::fmt; -use crate::mem; use crate::ptr::{Alignment, NonNull}; +use crate::{cmp, fmt, mem}; #[cfg(kani)] use crate::kani; @@ -30,7 +28,7 @@ const fn size_align() -> (usize, usize) { /// You build a `Layout` up as an input to give to an allocator. /// /// All layouts have an associated size and a power-of-two alignment. The size, when rounded up to -/// the nearest multiple of `align`, does not overflow isize (i.e., the rounded value will always be +/// the nearest multiple of `align`, does not overflow `isize` (i.e., the rounded value will always be /// less than or equal to `isize::MAX`). /// /// (Note that layouts are *not* required to have non-zero size, @@ -65,7 +63,7 @@ impl Layout { /// * `align` must be a power of two, /// /// * `size`, when rounded up to the nearest multiple of `align`, - /// must not overflow isize (i.e., the rounded value must be + /// must not overflow `isize` (i.e., the rounded value must be /// less than or equal to `isize::MAX`). #[stable(feature = "alloc_layout", since = "1.28.0")] #[rustc_const_stable(feature = "const_alloc_layout_size_align", since = "1.50.0")] @@ -188,6 +186,8 @@ impl Layout { /// - a [slice], then the length of the slice tail must be an initialized /// integer, and the size of the *entire value* /// (dynamic tail length + statically sized prefix) must fit in `isize`. + /// For the special case where the dynamic tail length is 0, this function + /// is safe to call. /// - a [trait object], then the vtable part of the pointer must point /// to a valid vtable for the type `T` acquired by an unsizing coercion, /// and the size of the *entire value* diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index 1c8e667654469..aa841db045ce7 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -17,10 +17,8 @@ pub use self::layout::Layout; )] #[allow(deprecated, deprecated_in_future)] pub use self::layout::LayoutErr; - #[stable(feature = "alloc_layout_error", since = "1.50.0")] pub use self::layout::LayoutError; - use crate::error::Error; use crate::fmt; use crate::ptr::{self, NonNull}; diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 37cb8e7d303af..58107b1e7d074 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -86,9 +86,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::fmt; -use crate::hash; -use crate::intrinsics; +use crate::{fmt, hash, intrinsics}; /////////////////////////////////////////////////////////////////////////////// // Any trait @@ -602,7 +600,7 @@ impl dyn Any + Send + Sync { /// While `TypeId` implements `Hash`, `PartialOrd`, and `Ord`, it is worth /// noting that the hashes and ordering will vary between Rust releases. Beware /// of relying on them inside of your code! -#[derive(Clone, Copy, Debug, Eq, PartialOrd, Ord)] +#[derive(Clone, Copy, Eq, PartialOrd, Ord)] #[stable(feature = "rust1", since = "1.0.0")] pub struct TypeId { // We avoid using `u128` because that imposes higher alignment requirements on many platforms. @@ -644,6 +642,10 @@ impl TypeId { let t2 = t as u64; TypeId { t: (t1, t2) } } + + fn as_u128(self) -> u128 { + u128::from(self.t.0) << 64 | u128::from(self.t.1) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -666,6 +668,13 @@ impl hash::Hash for TypeId { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for TypeId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "TypeId({:#034x})", self.as_u128()) + } +} + /// Returns the name of a type as a string slice. /// /// # Note diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index 31d6bc36fc8b9..d681bd124fe13 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -4,6 +4,15 @@ #[stable(feature = "simd_arch", since = "1.27.0")] pub use crate::core_arch::arch::*; +#[cfg(bootstrap)] +#[allow(dead_code)] +#[unstable(feature = "sha512_sm_x86", issue = "126624")] +fn dummy() { + // AArch64 also has a target feature named `sm4`, so we need `#![feature(sha512_sm_x86)]` in lib.rs + // But as the bootstrap compiler doesn't know about this feature yet, we need to convert it to a + // library feature until bootstrap gets bumped +} + /// Inline assembly. /// /// Refer to [Rust By Example] for a usage guide and the [reference] for diff --git a/library/core/src/array/ascii.rs b/library/core/src/array/ascii.rs index 3fea9a44049fb..05797b042ee4a 100644 --- a/library/core/src/array/ascii.rs +++ b/library/core/src/array/ascii.rs @@ -2,7 +2,7 @@ use crate::ascii; #[cfg(not(test))] impl [u8; N] { - /// Converts this array of bytes into a array of ASCII characters, + /// Converts this array of bytes into an array of ASCII characters, /// or returns `None` if any of the characters is non-ASCII. /// /// # Examples @@ -29,7 +29,7 @@ impl [u8; N] { } } - /// Converts this array of bytes into a array of ASCII characters, + /// Converts this array of bytes into an array of ASCII characters, /// without checking whether they're valid. /// /// # Safety diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index b314d0536a35a..2d19e4876f680 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -1,14 +1,11 @@ //! Defines the `IntoIter` owned iterator for arrays. +use crate::intrinsics::transmute_unchecked; +use crate::iter::{self, FusedIterator, TrustedLen, TrustedRandomAccessNoCoerce}; +use crate::mem::MaybeUninit; use crate::num::NonZero; -use crate::{ - fmt, - intrinsics::transmute_unchecked, - iter::{self, FusedIterator, TrustedLen, TrustedRandomAccessNoCoerce}, - mem::MaybeUninit, - ops::{IndexRange, Range}, - ptr, -}; +use crate::ops::{IndexRange, Range}; +use crate::{fmt, ptr}; /// A by-value [array] iterator. #[stable(feature = "array_value_iter", since = "1.51.0")] @@ -47,8 +44,10 @@ impl IntoIterator for [T; N] { type IntoIter = IntoIter; /// Creates a consuming iterator, that is, one that moves each value out of - /// the array (from start to end). The array cannot be used after calling - /// this unless `T` implements `Copy`, so the whole array is copied. + /// the array (from start to end). + /// + /// The array cannot be used after calling this unless `T` implements + /// `Copy`, so the whole array is copied. /// /// Arrays have special behavior when calling `.into_iter()` prior to the /// 2021 edition -- see the [array] Editions section for more information. @@ -101,7 +100,6 @@ impl IntoIter { /// ``` /// #![feature(array_into_iter_constructors)] /// #![feature(maybe_uninit_uninit_array_transpose)] - /// #![feature(maybe_uninit_uninit_array)] /// use std::array::IntoIter; /// use std::mem::MaybeUninit; /// @@ -111,7 +109,7 @@ impl IntoIter { /// fn next_chunk( /// it: &mut impl Iterator, /// ) -> Result<[T; N], IntoIter> { - /// let mut buffer = MaybeUninit::uninit_array(); + /// let mut buffer = [const { MaybeUninit::uninit() }; N]; /// let mut i = 0; /// while i < N { /// match it.next() { @@ -203,7 +201,7 @@ impl IntoIter { #[unstable(feature = "array_into_iter_constructors", issue = "91583")] #[rustc_const_unstable(feature = "const_array_into_iter_constructors", issue = "91583")] pub const fn empty() -> Self { - let buffer = MaybeUninit::uninit_array(); + let buffer = [const { MaybeUninit::uninit() }; N]; let initialized = 0..0; // SAFETY: We're telling it that none of the elements are initialized, @@ -405,7 +403,8 @@ impl Clone for IntoIter { fn clone(&self) -> Self { // Note, we don't really need to match the exact same alive range, so // we can just clone into offset 0 regardless of where `self` is. - let mut new = Self { data: MaybeUninit::uninit_array(), alive: IndexRange::zero_to(0) }; + let mut new = + Self { data: [const { MaybeUninit::uninit() }; N], alive: IndexRange::zero_to(0) }; // Clone all alive elements. for (src, dst) in iter::zip(self.as_slice(), &mut new.data) { diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 05874ab6c4cbb..5c826b9993f86 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -10,7 +10,7 @@ use crate::convert::Infallible; use crate::error::Error; use crate::fmt; use crate::hash::{self, Hash}; -use crate::iter::UncheckedIterator; +use crate::iter::{repeat_n, UncheckedIterator}; use crate::mem::{self, MaybeUninit}; use crate::ops::{ ChangeOutputType, ControlFlow, FromResidual, Index, IndexMut, NeverShortCircuit, Residual, Try, @@ -23,10 +23,36 @@ mod equality; mod iter; pub(crate) use drain::drain_array_with; - #[stable(feature = "array_value_iter", since = "1.51.0")] pub use iter::IntoIter; +/// Creates an array of type `[T; N]` by repeatedly cloning a value. +/// +/// This is the same as `[val; N]`, but it also works for types that do not +/// implement [`Copy`]. +/// +/// The provided value will be used as an element of the resulting array and +/// will be cloned N - 1 times to fill up the rest. If N is zero, the value +/// will be dropped. +/// +/// # Example +/// +/// Creating muliple copies of a `String`: +/// ```rust +/// #![feature(array_repeat)] +/// +/// use std::array; +/// +/// let string = "Hello there!".to_string(); +/// let strings = array::repeat(string); +/// assert_eq!(strings, ["Hello there!", "Hello there!"]); +/// ``` +#[inline] +#[unstable(feature = "array_repeat", issue = "126695")] +pub fn repeat(val: T) -> [T; N] { + from_trusted_iterator(repeat_n(val, N)) +} + /// Creates an array of type [T; N], where each element `T` is the returned value from `cb` /// using that element's index. /// @@ -100,7 +126,7 @@ where R: Try, R::Residual: Residual<[R::Output; N]>, { - let mut array = MaybeUninit::uninit_array::(); + let mut array = [const { MaybeUninit::uninit() }; N]; match try_from_fn_erased(&mut array, cb) { ControlFlow::Break(r) => FromResidual::from_residual(r), ControlFlow::Continue(()) => { @@ -533,11 +559,9 @@ impl [T; N] { /// assert_eq!(c, Some(a)); /// ``` #[unstable(feature = "array_try_map", issue = "79711")] - pub fn try_map(self, f: F) -> ChangeOutputType + pub fn try_map(self, f: impl FnMut(T) -> R) -> ChangeOutputType where - F: FnMut(T) -> R, - R: Try, - R::Residual: Residual<[R::Output; N]>, + R: Try>, { drain_array_with(self, |iter| try_from_trusted_iterator(iter.map(f))) } @@ -893,7 +917,7 @@ impl Drop for Guard<'_, T> { pub(crate) fn iter_next_chunk( iter: &mut impl Iterator, ) -> Result<[T; N], IntoIter> { - let mut array = MaybeUninit::uninit_array::(); + let mut array = [const { MaybeUninit::uninit() }; N]; let r = iter_next_chunk_erased(&mut array, iter); match r { Ok(()) => { diff --git a/library/core/src/ascii.rs b/library/core/src/ascii.rs index e9f4d0f93ed49..5b3711b4071ab 100644 --- a/library/core/src/ascii.rs +++ b/library/core/src/ascii.rs @@ -9,10 +9,9 @@ #![stable(feature = "core_ascii", since = "1.26.0")] -use crate::escape; -use crate::fmt; use crate::iter::FusedIterator; use crate::num::NonZero; +use crate::{escape, fmt}; mod ascii_char; #[unstable(feature = "ascii_char", issue = "110998")] diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index 34a05ac38884d..4d81eae9ccb83 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -3,9 +3,13 @@ //! suggestions from rustc if you get anything slightly wrong in here, and overall //! helps with clarity as we're also referring to `char` intentionally in here. +use safety::{ensures, requires}; use crate::fmt::{self, Write}; use crate::mem::transmute; +#[cfg(kani)] +use crate::kani; + /// One of the 128 Unicode characters from U+0000 through U+007F, /// often known as the [ASCII] subset. /// @@ -449,6 +453,7 @@ impl AsciiChar { /// or returns `None` if it's too large. #[unstable(feature = "ascii_char", issue = "110998")] #[inline] + #[ensures(|result| (b <= 127) == (result.is_some() && result.unwrap() as u8 == b))] pub const fn from_u8(b: u8) -> Option { if b <= 127 { // SAFETY: Just checked that `b` is in-range @@ -466,6 +471,8 @@ impl AsciiChar { /// `b` must be in `0..=127`, or else this is UB. #[unstable(feature = "ascii_char", issue = "110998")] #[inline] + #[requires(b <= 127)] + #[ensures(|result| *result as u8 == b)] pub const unsafe fn from_u8_unchecked(b: u8) -> Self { // SAFETY: Our safety precondition is that `b` is in-range. unsafe { transmute(b) } @@ -616,3 +623,22 @@ impl fmt::Debug for AsciiChar { f.write_char('\'') } } + +#[cfg(kani)] +#[unstable(feature="kani", issue="none")] +mod verify { + use super::*; + use AsciiChar; + + #[kani::proof_for_contract(AsciiChar::from_u8)] + fn check_from_u8() { + let b: u8 = kani::any(); + AsciiChar::from_u8(b); + } + + #[kani::proof_for_contract(AsciiChar::from_u8_unchecked)] + fn check_from_u8_unchecked() { + let b: u8 = kani::any(); + unsafe { AsciiChar::from_u8_unchecked(b) }; + } +} diff --git a/library/core/src/asserting.rs b/library/core/src/asserting.rs index 212b637d34365..3015aa562e6c0 100644 --- a/library/core/src/asserting.rs +++ b/library/core/src/asserting.rs @@ -9,10 +9,8 @@ #![doc(hidden)] #![unstable(feature = "generic_assert_internals", issue = "44838")] -use crate::{ - fmt::{Debug, Formatter}, - marker::PhantomData, -}; +use crate::fmt::{Debug, Formatter}; +use crate::marker::PhantomData; // ***** TryCapture - Generic ***** diff --git a/library/core/src/async_iter/async_iter.rs b/library/core/src/async_iter/async_iter.rs index a0ffb6d47507b..069c50c2531b0 100644 --- a/library/core/src/async_iter/async_iter.rs +++ b/library/core/src/async_iter/async_iter.rs @@ -18,7 +18,7 @@ pub trait AsyncIterator { /// The type of items yielded by the async iterator. type Item; - /// Attempt to pull out the next value of this async iterator, registering the + /// Attempts to pull out the next value of this async iterator, registering the /// current task for wakeup if the value is not yet available, and returning /// `None` if the async iterator is exhausted. /// @@ -139,7 +139,7 @@ impl Poll> { pub const FINISHED: Self = Poll::Ready(None); } -/// Convert something into an async iterator +/// Converts something into an async iterator #[unstable(feature = "async_iterator", issue = "79024")] pub trait IntoAsyncIterator { /// The type of the item yielded by the iterator diff --git a/library/core/src/async_iter/from_iter.rs b/library/core/src/async_iter/from_iter.rs index 3180187afc8c9..f4e10bdffea2f 100644 --- a/library/core/src/async_iter/from_iter.rs +++ b/library/core/src/async_iter/from_iter.rs @@ -1,6 +1,5 @@ -use crate::pin::Pin; - use crate::async_iter::AsyncIterator; +use crate::pin::Pin; use crate::task::{Context, Poll}; /// An async iterator that was created from iterator. diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs index bc026d0a44634..ccb1cc4e974d6 100644 --- a/library/core/src/borrow.rs +++ b/library/core/src/borrow.rs @@ -184,6 +184,7 @@ pub trait Borrow { /// an underlying type by providing a mutable reference. See [`Borrow`] /// for more information on borrowing as another type. #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "BorrowMut"] pub trait BorrowMut: Borrow { /// Mutably borrows from an owned value. /// diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 4b491ffdafa70..0d66c2b52c84e 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -82,6 +82,20 @@ //! //! The corresponding [`Sync`] version of `OnceCell` is [`OnceLock`]. //! +//! ## `LazyCell` +//! +//! A common pattern with OnceCell is, for a given OnceCell, to use the same function on every +//! call to [`OnceCell::get_or_init`] with that cell. This is what is offered by [`LazyCell`], +//! which pairs cells of `T` with functions of `F`, and always calls `F` before it yields `&T`. +//! This happens implicitly by simply attempting to dereference the LazyCell to get its contents, +//! so its use is much more transparent with a place which has been initialized by a constant. +//! +//! More complicated patterns that don't fit this description can be built on `OnceCell` instead. +//! +//! `LazyCell` works by providing an implementation of `impl Deref` that calls the function, +//! so you can just use it by dereference (e.g. `*lazy_cell` or `lazy_cell.deref()`). +//! +//! The corresponding [`Sync`] version of `LazyCell` is [`LazyLock`]. //! //! # When to choose interior mutability //! @@ -230,6 +244,7 @@ //! [`RwLock`]: ../../std/sync/struct.RwLock.html //! [`Mutex`]: ../../std/sync/struct.Mutex.html //! [`OnceLock`]: ../../std/sync/struct.OnceLock.html +//! [`LazyLock`]: ../../std/sync/struct.LazyLock.html //! [`Sync`]: ../../std/marker/trait.Sync.html //! [`atomic`]: crate::sync::atomic @@ -238,14 +253,14 @@ use crate::cmp::Ordering; use crate::fmt::{self, Debug, Display}; use crate::marker::{PhantomData, Unsize}; -use crate::mem::{self, size_of}; +use crate::mem; use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; use crate::ptr::{self, NonNull}; mod lazy; mod once; -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] pub use lazy::LazyCell; #[stable(feature = "once_cell", since = "1.70.0")] pub use once::OnceCell; @@ -412,7 +427,9 @@ impl Cell { } /// Swaps the values of two `Cell`s. - /// Difference with `std::mem::swap` is that this function doesn't require `&mut` reference. + /// + /// The difference with `std::mem::swap` is that this function doesn't + /// require a `&mut` reference. /// /// # Panics /// @@ -1564,7 +1581,7 @@ impl<'b, T: ?Sized> Ref<'b, T> { ) } - /// Convert into a reference to the underlying data. + /// Converts into a reference to the underlying data. /// /// The underlying `RefCell` can never be mutably borrowed from again and will always appear /// already immutably borrowed. It is not a good idea to leak more than a constant number of @@ -1732,7 +1749,7 @@ impl<'b, T: ?Sized> RefMut<'b, T> { ) } - /// Convert into a mutable reference to the underlying data. + /// Converts into a mutable reference to the underlying data. /// /// The underlying `RefCell` can not be borrowed from again and will always appear already /// mutably borrowed, making the returned reference the only to the interior. @@ -1864,7 +1881,7 @@ impl fmt::Display for RefMut<'_, T> { /// /// If you have a reference `&T`, then normally in Rust the compiler performs optimizations based on /// the knowledge that `&T` points to immutable data. Mutating that data, for example through an -/// alias or by transmuting an `&T` into an `&mut T`, is considered undefined behavior. +/// alias or by transmuting a `&T` into a `&mut T`, is considered undefined behavior. /// `UnsafeCell` opts-out of the immutability guarantee for `&T`: a shared reference /// `&UnsafeCell` may point to data that is being mutated. This is called "interior mutability". /// @@ -1921,7 +1938,7 @@ impl fmt::Display for RefMut<'_, T> { /// to have multiple `&mut UnsafeCell` aliases. That is, `UnsafeCell` is a wrapper /// designed to have a special interaction with _shared_ accesses (_i.e._, through an /// `&UnsafeCell<_>` reference); there is no magic whatsoever when dealing with _exclusive_ -/// accesses (_e.g._, through an `&mut UnsafeCell<_>`): neither the cell nor the wrapped value +/// accesses (_e.g._, through a `&mut UnsafeCell<_>`): neither the cell nor the wrapped value /// may be aliased for the duration of that `&mut` borrow. /// This is showcased by the [`.get_mut()`] accessor, which is a _safe_ getter that yields /// a `&mut T`. diff --git a/library/core/src/cell/lazy.rs b/library/core/src/cell/lazy.rs index 1b213f6a2941b..6ec1d2a33bef5 100644 --- a/library/core/src/cell/lazy.rs +++ b/library/core/src/cell/lazy.rs @@ -1,8 +1,7 @@ +use super::UnsafeCell; use crate::ops::Deref; use crate::{fmt, mem}; -use super::UnsafeCell; - enum State { Uninit(F), Init(T), @@ -18,8 +17,6 @@ enum State { /// # Examples /// /// ``` -/// #![feature(lazy_cell)] -/// /// use std::cell::LazyCell; /// /// let lazy: LazyCell = LazyCell::new(|| { @@ -36,7 +33,7 @@ enum State { /// // 92 /// // 92 /// ``` -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] pub struct LazyCell T> { state: UnsafeCell>, } @@ -47,8 +44,6 @@ impl T> LazyCell { /// # Examples /// /// ``` - /// #![feature(lazy_cell)] - /// /// use std::cell::LazyCell; /// /// let hello = "Hello, World!".to_string(); @@ -58,7 +53,8 @@ impl T> LazyCell { /// assert_eq!(&*lazy, "HELLO, WORLD!"); /// ``` #[inline] - #[unstable(feature = "lazy_cell", issue = "109736")] + #[stable(feature = "lazy_cell", since = "1.80.0")] + #[rustc_const_stable(feature = "lazy_cell", since = "1.80.0")] pub const fn new(f: F) -> LazyCell { LazyCell { state: UnsafeCell::new(State::Uninit(f)) } } @@ -70,8 +66,7 @@ impl T> LazyCell { /// # Examples /// /// ``` - /// #![feature(lazy_cell)] - /// #![feature(lazy_cell_consume)] + /// #![feature(lazy_cell_into_inner)] /// /// use std::cell::LazyCell; /// @@ -82,7 +77,7 @@ impl T> LazyCell { /// assert_eq!(&*lazy, "HELLO, WORLD!"); /// assert_eq!(LazyCell::into_inner(lazy).ok(), Some("HELLO, WORLD!".to_string())); /// ``` - #[unstable(feature = "lazy_cell_consume", issue = "109736")] + #[unstable(feature = "lazy_cell_into_inner", issue = "125623")] pub fn into_inner(this: Self) -> Result { match this.state.into_inner() { State::Init(data) => Ok(data), @@ -99,8 +94,6 @@ impl T> LazyCell { /// # Examples /// /// ``` - /// #![feature(lazy_cell)] - /// /// use std::cell::LazyCell; /// /// let lazy = LazyCell::new(|| 92); @@ -109,7 +102,7 @@ impl T> LazyCell { /// assert_eq!(&*lazy, &92); /// ``` #[inline] - #[unstable(feature = "lazy_cell", issue = "109736")] + #[stable(feature = "lazy_cell", since = "1.80.0")] pub fn force(this: &LazyCell) -> &T { // SAFETY: // This invalidates any mutable references to the data. The resulting @@ -173,7 +166,7 @@ impl LazyCell { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl T> Deref for LazyCell { type Target = T; #[inline] @@ -182,7 +175,7 @@ impl T> Deref for LazyCell { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl Default for LazyCell { /// Creates a new lazy value using `Default` as the initializing function. #[inline] @@ -191,7 +184,7 @@ impl Default for LazyCell { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl fmt::Debug for LazyCell { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_tuple("LazyCell"); diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index a7c3dfc982d12..097fa86c93814 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -1,13 +1,12 @@ use crate::cell::UnsafeCell; -use crate::fmt; -use crate::mem; +use crate::{fmt, mem}; -/// A cell which can be written to only once. +/// A cell which can nominally be written to only once. /// /// This allows obtaining a shared `&T` reference to its inner value without copying or replacing /// it (unlike [`Cell`]), and without runtime borrow checks (unlike [`RefCell`]). However, /// only immutable references can be obtained unless one has a mutable reference to the cell -/// itself. +/// itself. In the same vein, the cell can only be re-initialized with such a mutable reference. /// /// For a thread-safe version of this struct, see [`std::sync::OnceLock`]. /// diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index f0c2636307fcf..698c85fcd1301 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -1,5 +1,6 @@ //! Character conversions. +use safety::{requires, ensures}; use crate::char::TryFromCharError; use crate::error::Error; use crate::fmt; @@ -7,6 +8,9 @@ use crate::mem::transmute; use crate::str::FromStr; use crate::ub_checks::assert_unsafe_precondition; +#[cfg(kani)] +use crate::kani; + /// Converts a `u32` to a `char`. See [`char::from_u32`]. #[must_use] #[inline] @@ -21,6 +25,8 @@ pub(super) const fn from_u32(i: u32) -> Option { /// Converts a `u32` to a `char`, ignoring validity. See [`char::from_u32_unchecked`]. #[inline] #[must_use] +#[requires(char_try_from_u32(i).is_ok())] +#[ensures(|result| *result as u32 == i)] pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { // SAFETY: the caller must guarantee that `i` is a valid char value. unsafe { @@ -290,3 +296,15 @@ pub(super) const fn from_digit(num: u32, radix: u32) -> Option { None } } + +#[cfg(kani)] +#[unstable(feature="kani", issue="none")] +mod verify { + use super::*; + + #[kani::proof_for_contract(from_u32_unchecked)] + fn check_from_u32_unchecked() { + let i: u32 = kani::any(); + unsafe { from_u32_unchecked(i) }; + } +} diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 458be49fb152a..cbda18b5ac2eb 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1,11 +1,13 @@ //! impl char {} +use super::*; use crate::slice; use crate::str::from_utf8_unchecked_mut; use crate::unicode::printable::is_printable; use crate::unicode::{self, conversions}; -use super::*; +#[cfg(kani)] +use crate::kani; impl char { /// The lowest valid code point a `char` can have, `'\0'`. @@ -223,7 +225,7 @@ impl char { /// assert_eq!('❤', c); /// ``` #[stable(feature = "assoc_char_funcs", since = "1.52.0")] - #[rustc_const_unstable(feature = "const_char_from_u32_unchecked", issue = "89259")] + #[rustc_const_stable(feature = "const_char_from_u32_unchecked", since = "1.81.0")] #[must_use] #[inline] pub const unsafe fn from_u32_unchecked(i: u32) -> char { @@ -1833,3 +1835,27 @@ pub fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] { } } } + +#[cfg(kani)] +#[unstable(feature="kani", issue="none")] +mod verify { + use super::*; + use safety::ensures; + + #[ensures(|result| c.is_ascii() == (result.is_some() && (result.unwrap() as u8 as char == *c)))] + fn as_ascii_clone(c: &char) -> Option { + c.as_ascii() + } + + #[kani::proof_for_contract(as_ascii_clone)] + fn check_as_ascii_ascii_char() { + let ascii: char = kani::any_where(|c : &char| c.is_ascii()); + as_ascii_clone(&ascii); + } + + #[kani::proof_for_contract(as_ascii_clone)] + fn check_as_ascii_non_ascii_char() { + let non_ascii: char = kani::any_where(|c: &char| !c.is_ascii()); + as_ascii_clone(&non_ascii); + } +} diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index f3683fe3f9c83..e6574ac3c7278 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -25,6 +25,7 @@ mod decode; mod methods; // stable re-exports +#[rustfmt::skip] #[stable(feature = "try_from", since = "1.34.0")] pub use self::convert::CharTryFromError; #[stable(feature = "char_from_str", since = "1.20.0")] @@ -33,20 +34,21 @@ pub use self::convert::ParseCharError; pub use self::decode::{DecodeUtf16, DecodeUtf16Error}; // perma-unstable re-exports +#[rustfmt::skip] #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::methods::encode_utf16_raw; +pub use self::methods::encode_utf16_raw; // perma-unstable #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::methods::encode_utf8_raw; +pub use self::methods::encode_utf8_raw; // perma-unstable +#[rustfmt::skip] use crate::ascii; +pub(crate) use self::methods::EscapeDebugExtArgs; use crate::error::Error; use crate::escape; use crate::fmt::{self, Write}; use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce}; use crate::num::NonZero; -pub(crate) use self::methods::EscapeDebugExtArgs; - // UTF-8 ranges and tags for encoding characters const TAG_CONT: u8 = 0b1000_0000; const TAG_TWO_B: u8 = 0b1100_0000; @@ -123,7 +125,7 @@ pub const fn from_u32(i: u32) -> Option { /// Converts a `u32` to a `char`, ignoring validity. Use [`char::from_u32_unchecked`]. /// instead. #[stable(feature = "char_from_unchecked", since = "1.5.0")] -#[rustc_const_unstable(feature = "const_char_from_u32_unchecked", issue = "89259")] +#[rustc_const_stable(feature = "const_char_from_u32_unchecked", since = "1.81.0")] #[must_use] #[inline] pub const unsafe fn from_u32_unchecked(i: u32) -> char { diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index d448c5338fc46..76a89eaaff86e 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -36,6 +36,9 @@ #![stable(feature = "rust1", since = "1.0.0")] +use crate::mem::{self, MaybeUninit}; +use crate::ptr; + /// A common trait for the ability to explicitly duplicate an object. /// /// Differs from [`Copy`] in that [`Copy`] is implicit and an inexpensive bit-wise copy, while @@ -157,6 +160,9 @@ pub trait Clone: Sized { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "cloning is often expensive and is not expected to have side effects"] + // Clone::clone is special because the compiler generates MIR to implement it for some types. + // See InstanceKind::CloneShim. + #[cfg_attr(not(bootstrap), lang = "clone_fn")] fn clone(&self) -> Self; /// Performs copy-assignment from `source`. @@ -204,6 +210,189 @@ pub struct AssertParamIsCopy { _field: crate::marker::PhantomData, } +/// A generalization of [`Clone`] to dynamically-sized types stored in arbitrary containers. +/// +/// This trait is implemented for all types implementing [`Clone`], and also [slices](slice) of all +/// such types. You may also implement this trait to enable cloning trait objects and custom DSTs +/// (structures containing dynamically-sized fields). +/// +/// # Safety +/// +/// Implementations must ensure that when `.clone_to_uninit(dst)` returns normally rather than +/// panicking, it always leaves `*dst` initialized as a valid value of type `Self`. +/// +/// # See also +/// +/// * [`Clone::clone_from`] is a safe function which may be used instead when `Self` is a [`Sized`] +/// and the destination is already initialized; it may be able to reuse allocations owned by +/// the destination. +/// * [`ToOwned`], which allocates a new destination container. +/// +/// [`ToOwned`]: ../../std/borrow/trait.ToOwned.html +#[unstable(feature = "clone_to_uninit", issue = "126799")] +pub unsafe trait CloneToUninit { + /// Performs copy-assignment from `self` to `dst`. + /// + /// This is analogous to `std::ptr::write(dst, self.clone())`, + /// except that `self` may be a dynamically-sized type ([`!Sized`](Sized)). + /// + /// Before this function is called, `dst` may point to uninitialized memory. + /// After this function is called, `dst` will point to initialized memory; it will be + /// sound to create a `&Self` reference from the pointer. + /// + /// # Safety + /// + /// Behavior is undefined if any of the following conditions are violated: + /// + /// * `dst` must be [valid] for writes. + /// * `dst` must be properly aligned. + /// * `dst` must have the same [pointer metadata] (slice length or `dyn` vtable) as `self`. + /// + /// [valid]: ptr#safety + /// [pointer metadata]: crate::ptr::metadata() + /// + /// # Panics + /// + /// This function may panic. (For example, it might panic if memory allocation for a clone + /// of a value owned by `self` fails.) + /// If the call panics, then `*dst` should be treated as uninitialized memory; it must not be + /// read or dropped, because even if it was previously valid, it may have been partially + /// overwritten. + /// + /// The caller may also need to take care to deallocate the allocation pointed to by `dst`, + /// if applicable, to avoid a memory leak, and may need to take other precautions to ensure + /// soundness in the presence of unwinding. + /// + /// Implementors should avoid leaking values by, upon unwinding, dropping all component values + /// that might have already been created. (For example, if a `[Foo]` of length 3 is being + /// cloned, and the second of the three calls to `Foo::clone()` unwinds, then the first `Foo` + /// cloned should be dropped.) + unsafe fn clone_to_uninit(&self, dst: *mut Self); +} + +#[unstable(feature = "clone_to_uninit", issue = "126799")] +unsafe impl CloneToUninit for T { + default unsafe fn clone_to_uninit(&self, dst: *mut Self) { + // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of + // ptr::write(). + unsafe { + // We hope the optimizer will figure out to create the cloned value in-place, + // skipping ever storing it on the stack and the copy to the destination. + ptr::write(dst, self.clone()); + } + } +} + +// Specialized implementation for types that are [`Copy`], not just [`Clone`], +// and can therefore be copied bitwise. +#[unstable(feature = "clone_to_uninit", issue = "126799")] +unsafe impl CloneToUninit for T { + unsafe fn clone_to_uninit(&self, dst: *mut Self) { + // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of + // ptr::copy_nonoverlapping(). + unsafe { + ptr::copy_nonoverlapping(self, dst, 1); + } + } +} + +#[unstable(feature = "clone_to_uninit", issue = "126799")] +unsafe impl CloneToUninit for [T] { + #[cfg_attr(debug_assertions, track_caller)] + default unsafe fn clone_to_uninit(&self, dst: *mut Self) { + let len = self.len(); + // This is the most likely mistake to make, so check it as a debug assertion. + debug_assert_eq!( + len, + dst.len(), + "clone_to_uninit() source and destination must have equal lengths", + ); + + // SAFETY: The produced `&mut` is valid because: + // * The caller is obligated to provide a pointer which is valid for writes. + // * All bytes pointed to are in MaybeUninit, so we don't care about the memory's + // initialization status. + let uninit_ref = unsafe { &mut *(dst as *mut [MaybeUninit]) }; + + // Copy the elements + let mut initializing = InitializingSlice::from_fully_uninit(uninit_ref); + for element_ref in self.iter() { + // If the clone() panics, `initializing` will take care of the cleanup. + initializing.push(element_ref.clone()); + } + // If we reach here, then the entire slice is initialized, and we've satisfied our + // responsibilities to the caller. Disarm the cleanup guard by forgetting it. + mem::forget(initializing); + } +} + +#[unstable(feature = "clone_to_uninit", issue = "126799")] +unsafe impl CloneToUninit for [T] { + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_to_uninit(&self, dst: *mut Self) { + let len = self.len(); + // This is the most likely mistake to make, so check it as a debug assertion. + debug_assert_eq!( + len, + dst.len(), + "clone_to_uninit() source and destination must have equal lengths", + ); + + // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of + // ptr::copy_nonoverlapping(). + unsafe { + ptr::copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), len); + } + } +} + +/// Ownership of a collection of values stored in a non-owned `[MaybeUninit]`, some of which +/// are not yet initialized. This is sort of like a `Vec` that doesn't own its allocation. +/// Its responsibility is to provide cleanup on unwind by dropping the values that *are* +/// initialized, unless disarmed by forgetting. +/// +/// This is a helper for `impl CloneToUninit for [T]`. +struct InitializingSlice<'a, T> { + data: &'a mut [MaybeUninit], + /// Number of elements of `*self.data` that are initialized. + initialized_len: usize, +} + +impl<'a, T> InitializingSlice<'a, T> { + #[inline] + fn from_fully_uninit(data: &'a mut [MaybeUninit]) -> Self { + Self { data, initialized_len: 0 } + } + + /// Push a value onto the end of the initialized part of the slice. + /// + /// # Panics + /// + /// Panics if the slice is already fully initialized. + #[inline] + fn push(&mut self, value: T) { + MaybeUninit::write(&mut self.data[self.initialized_len], value); + self.initialized_len += 1; + } +} + +impl<'a, T> Drop for InitializingSlice<'a, T> { + #[cold] // will only be invoked on unwind + fn drop(&mut self) { + let initialized_slice = ptr::slice_from_raw_parts_mut( + MaybeUninit::slice_as_mut_ptr(self.data), + self.initialized_len, + ); + // SAFETY: + // * the pointer is valid because it was made from a mutable reference + // * `initialized_len` counts the initialized elements as an invariant of this type, + // so each of the pointed-to elements is initialized and may be dropped. + unsafe { + ptr::drop_in_place::<[T]>(initialized_slice); + } + } +} + /// Implementations of `Clone` for primitive types. /// /// Implementations that cannot be described in Rust diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index f3f757ce69df7..a1ed993b7d9bf 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -245,17 +245,15 @@ use self::Ordering::*; append_const_msg )] #[rustc_diagnostic_item = "PartialEq"] -#[const_trait] pub trait PartialEq { - /// This method tests for `self` and `other` values to be equal, and is used - /// by `==`. + /// Tests for `self` and `other` values to be equal, and is used by `==`. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "cmp_partialeq_eq"] fn eq(&self, other: &Rhs) -> bool; - /// This method tests for `!=`. The default implementation is almost always - /// sufficient, and should not be overridden without very good reason. + /// Tests for `!=`. The default implementation is almost always sufficient, + /// and should not be overridden without very good reason. #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] @@ -1164,7 +1162,7 @@ pub trait PartialOrd: PartialEq { #[rustc_diagnostic_item = "cmp_partialord_cmp"] fn partial_cmp(&self, other: &Rhs) -> Option; - /// This method tests less than (for `self` and `other`) and is used by the `<` operator. + /// Tests less than (for `self` and `other`) and is used by the `<` operator. /// /// # Examples /// @@ -1181,8 +1179,8 @@ pub trait PartialOrd: PartialEq { matches!(self.partial_cmp(other), Some(Less)) } - /// This method tests less than or equal to (for `self` and `other`) and is used by the `<=` - /// operator. + /// Tests less than or equal to (for `self` and `other`) and is used by the + /// `<=` operator. /// /// # Examples /// @@ -1199,7 +1197,8 @@ pub trait PartialOrd: PartialEq { matches!(self.partial_cmp(other), Some(Less | Equal)) } - /// This method tests greater than (for `self` and `other`) and is used by the `>` operator. + /// Tests greater than (for `self` and `other`) and is used by the `>` + /// operator. /// /// # Examples /// @@ -1216,8 +1215,8 @@ pub trait PartialOrd: PartialEq { matches!(self.partial_cmp(other), Some(Greater)) } - /// This method tests greater than or equal to (for `self` and `other`) and is used by the `>=` - /// operator. + /// Tests greater than or equal to (for `self` and `other`) and is used by + /// the `>=` operator. /// /// # Examples /// @@ -1475,8 +1474,7 @@ mod impls { macro_rules! partial_eq_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_cmp", issue = "92391")] - impl const PartialEq for $t { + impl PartialEq for $t { #[inline] fn eq(&self, other: &$t) -> bool { (*self) == (*other) } #[inline] diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index 86c4ea9fab088..0246d0627cafe 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -208,7 +208,7 @@ macro_rules! impl_try_from_unbounded { impl TryFrom<$source> for $target { type Error = TryFromIntError; - /// Try to create the target number type from a source + /// Tries to create the target number type from a source /// number type. This returns an error if the source value /// is outside of the range of the target type. #[inline] @@ -226,7 +226,7 @@ macro_rules! impl_try_from_lower_bounded { impl TryFrom<$source> for $target { type Error = TryFromIntError; - /// Try to create the target number type from a source + /// Tries to create the target number type from a source /// number type. This returns an error if the source value /// is outside of the range of the target type. #[inline] @@ -248,7 +248,7 @@ macro_rules! impl_try_from_upper_bounded { impl TryFrom<$source> for $target { type Error = TryFromIntError; - /// Try to create the target number type from a source + /// Tries to create the target number type from a source /// number type. This returns an error if the source value /// is outside of the range of the target type. #[inline] @@ -270,7 +270,7 @@ macro_rules! impl_try_from_both_bounded { impl TryFrom<$source> for $target { type Error = TryFromIntError; - /// Try to create the target number type from a source + /// Tries to create the target number type from a source /// number type. This returns an error if the source value /// is outside of the range of the target type. #[inline] diff --git a/library/core/src/default.rs b/library/core/src/default.rs index 4524b352ec817..5cacedcb241a5 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -103,6 +103,7 @@ use crate::ascii::Char as AsciiChar; /// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "Default")] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(bootstrap), rustc_trivial_field_reads)] pub trait Default: Sized { /// Returns the "default value" for a type. /// diff --git a/library/core/src/error.md b/library/core/src/error.md index a5deb71e6b80a..4b62391cafc37 100644 --- a/library/core/src/error.md +++ b/library/core/src/error.md @@ -17,8 +17,8 @@ The following are the primary interfaces of the panic system and the responsibilities they cover: * [`panic!`] and [`panic_any`] (Constructing, Propagated automatically) -* [`PanicInfo`] (Reporting) -* [`set_hook`], [`take_hook`], and [`#[panic_handler]`][panic-handler] (Reporting) +* [`set_hook`], [`take_hook`], and [`PanicHookInfo`] (Reporting) +* [`#[panic_handler]`][panic-handler] and [`PanicInfo`] (Reporting in no_std) * [`catch_unwind`] and [`resume_unwind`] (Discarding, Propagating) The following are the primary interfaces of the error system and the @@ -125,6 +125,7 @@ expect-as-precondition style error messages remember to focus on the word should be available and executable by the current user". [`panic_any`]: ../../std/panic/fn.panic_any.html +[`PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html [`PanicInfo`]: crate::panic::PanicInfo [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html [`resume_unwind`]: ../../std/panic/fn.resume_unwind.html diff --git a/library/core/src/error.rs b/library/core/src/error.rs index a3f2b767054e1..6cc91849e1dc9 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -1,5 +1,5 @@ #![doc = include_str!("error.md")] -#![unstable(feature = "error_in_core", issue = "103765")] +#![stable(feature = "error_in_core", since = "1.81.0")] #[cfg(test)] mod tests; @@ -30,7 +30,7 @@ use crate::fmt::{Debug, Display, Formatter, Result}; #[rustc_has_incoherent_inherent_impls] #[allow(multiple_supertrait_upcastable)] pub trait Error: Debug + Display { - /// The lower-level source of this error, if any. + /// Returns the lower-level source of this error, if any. /// /// # Examples /// @@ -121,7 +121,7 @@ pub trait Error: Debug + Display { self.source() } - /// Provides type based access to context intended for error reports. + /// Provides type-based access to context intended for error reports. /// /// Used in conjunction with [`Request::provide_value`] and [`Request::provide_ref`] to extract /// references to member variables from `dyn Error` trait objects. @@ -130,7 +130,6 @@ pub trait Error: Debug + Display { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// use core::fmt; /// use core::error::{request_ref, Request}; /// @@ -354,15 +353,14 @@ impl dyn Error { } } -/// Request a value of type `T` from the given `impl Error`. +/// Requests a value of type `T` from the given `impl Error`. /// /// # Examples /// /// Get a string value from an error. /// /// ```rust -/// # #![feature(error_generic_member_access)] -/// # #![feature(error_in_core)] +/// #![feature(error_generic_member_access)] /// use std::error::Error; /// use core::error::request_value; /// @@ -378,15 +376,14 @@ where request_by_type_tag::<'a, tags::Value>(err) } -/// Request a reference of type `T` from the given `impl Error`. +/// Requests a reference of type `T` from the given `impl Error`. /// /// # Examples /// /// Get a string reference from an error. /// /// ```rust -/// # #![feature(error_generic_member_access)] -/// # #![feature(error_in_core)] +/// #![feature(error_generic_member_access)] /// use core::error::Error; /// use core::error::request_ref; /// @@ -407,9 +404,9 @@ fn request_by_type_tag<'a, I>(err: &'a (impl Error + ?Sized)) -> Option, { - let mut tagged = TaggedOption::<'a, I>(None); + let mut tagged = Tagged { tag_id: TypeId::of::(), value: TaggedOption::<'a, I>(None) }; err.provide(tagged.as_request()); - tagged.0 + tagged.value.0 } /////////////////////////////////////////////////////////////////////////////// @@ -458,7 +455,6 @@ where /// /// ``` /// #![feature(error_generic_member_access)] -/// #![feature(error_in_core)] /// use core::fmt; /// use core::error::Request; /// use core::error::request_ref; @@ -510,18 +506,11 @@ where /// ``` /// #[unstable(feature = "error_generic_member_access", issue = "99301")] -#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 -pub struct Request<'a>(dyn Erased<'a> + 'a); +#[repr(transparent)] +pub struct Request<'a>(Tagged + 'a>); impl<'a> Request<'a> { - /// Create a new `&mut Request` from a `&mut dyn Erased` trait object. - fn new<'b>(erased: &'b mut (dyn Erased<'a> + 'a)) -> &'b mut Request<'a> { - // SAFETY: transmuting `&mut (dyn Erased<'a> + 'a)` to `&mut Request<'a>` is safe since - // `Request` is repr(transparent). - unsafe { &mut *(erased as *mut dyn Erased<'a> as *mut Request<'a>) } - } - - /// Provide a value or other type with only static lifetimes. + /// Provides a value or other type with only static lifetimes. /// /// # Examples /// @@ -529,7 +518,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// @@ -556,7 +544,7 @@ impl<'a> Request<'a> { self.provide::>(value) } - /// Provide a value or other type with only static lifetimes computed using a closure. + /// Provides a value or other type with only static lifetimes computed using a closure. /// /// # Examples /// @@ -564,7 +552,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// @@ -591,7 +578,7 @@ impl<'a> Request<'a> { self.provide_with::>(fulfil) } - /// Provide a reference. The referee type must be bounded by `'static`, + /// Provides a reference. The referee type must be bounded by `'static`, /// but may be unsized. /// /// # Examples @@ -600,7 +587,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// @@ -624,7 +610,7 @@ impl<'a> Request<'a> { self.provide::>>(value) } - /// Provide a reference computed using a closure. The referee type + /// Provides a reference computed using a closure. The referee type /// must be bounded by `'static`, but may be unsized. /// /// # Examples @@ -633,7 +619,6 @@ impl<'a> Request<'a> { /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// @@ -667,7 +652,7 @@ impl<'a> Request<'a> { self.provide_with::>>(fulfil) } - /// Provide a value with the given `Type` tag. + /// Provides a value with the given `Type` tag. fn provide(&mut self, value: I::Reified) -> &mut Self where I: tags::Type<'a>, @@ -678,7 +663,7 @@ impl<'a> Request<'a> { self } - /// Provide a value with the given `Type` tag, using a closure to prevent unnecessary work. + /// Provides a value with the given `Type` tag, using a closure to prevent unnecessary work. fn provide_with(&mut self, fulfil: impl FnOnce() -> I::Reified) -> &mut Self where I: tags::Type<'a>, @@ -689,18 +674,17 @@ impl<'a> Request<'a> { self } - /// Check if the `Request` would be satisfied if provided with a + /// Checks if the `Request` would be satisfied if provided with a /// value of the specified type. If the type does not match or has /// already been provided, returns false. /// /// # Examples /// - /// Check if an `u8` still needs to be provided and then provides + /// Checks if a `u8` still needs to be provided and then provides /// it. /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// use core::error::request_value; @@ -777,18 +761,18 @@ impl<'a> Request<'a> { self.would_be_satisfied_by::>() } - /// Check if the `Request` would be satisfied if provided with a - /// reference to a value of the specified type. If the type does - /// not match or has already been provided, returns false. + /// Checks if the `Request` would be satisfied if provided with a + /// reference to a value of the specified type. + /// + /// If the type does not match or has already been provided, returns false. /// /// # Examples /// - /// Check if a `&str` still needs to be provided and then provides + /// Checks if a `&str` still needs to be provided and then provides /// it. /// /// ```rust /// #![feature(error_generic_member_access)] - /// #![feature(error_in_core)] /// /// use core::error::Request; /// use core::error::request_ref; @@ -945,32 +929,33 @@ pub(crate) mod tags { /// An `Option` with a type tag `I`. /// /// Since this struct implements `Erased`, the type can be erased to make a dynamically typed -/// option. The type can be checked dynamically using `Erased::tag_id` and since this is statically +/// option. The type can be checked dynamically using `Tagged::tag_id` and since this is statically /// checked for the concrete type, there is some degree of type safety. #[repr(transparent)] pub(crate) struct TaggedOption<'a, I: tags::Type<'a>>(pub Option); -impl<'a, I: tags::Type<'a>> TaggedOption<'a, I> { +impl<'a, I: tags::Type<'a>> Tagged> { pub(crate) fn as_request(&mut self) -> &mut Request<'a> { - Request::new(self as &mut (dyn Erased<'a> + 'a)) + let erased = self as &mut Tagged + 'a>; + // SAFETY: transmuting `&mut Tagged + 'a>` to `&mut Request<'a>` is safe since + // `Request` is repr(transparent). + unsafe { &mut *(erased as *mut Tagged> as *mut Request<'a>) } } } /// Represents a type-erased but identifiable object. /// /// This trait is exclusively implemented by the `TaggedOption` type. -unsafe trait Erased<'a>: 'a { - /// The `TypeId` of the erased type. - fn tag_id(&self) -> TypeId; -} +unsafe trait Erased<'a>: 'a {} -unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> { - fn tag_id(&self) -> TypeId { - TypeId::of::() - } +unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> {} + +struct Tagged { + tag_id: TypeId, + value: E, } -impl<'a> dyn Erased<'a> + 'a { +impl<'a> Tagged + 'a> { /// Returns some reference to the dynamic value if it is tagged with `I`, /// or `None` otherwise. #[inline] @@ -978,9 +963,9 @@ impl<'a> dyn Erased<'a> + 'a { where I: tags::Type<'a>, { - if self.tag_id() == TypeId::of::() { + if self.tag_id == TypeId::of::() { // SAFETY: Just checked whether we're pointing to an I. - Some(unsafe { &*(self as *const Self).cast::>() }) + Some(&unsafe { &*(self as *const Self).cast::>>() }.value) } else { None } @@ -993,9 +978,12 @@ impl<'a> dyn Erased<'a> + 'a { where I: tags::Type<'a>, { - if self.tag_id() == TypeId::of::() { - // SAFETY: Just checked whether we're pointing to an I. - Some(unsafe { &mut *(self as *mut Self).cast::>() }) + if self.tag_id == TypeId::of::() { + Some( + // SAFETY: Just checked whether we're pointing to an I. + &mut unsafe { &mut *(self as *mut Self).cast::>>() } + .value, + ) } else { None } @@ -1021,8 +1009,15 @@ impl<'a> Iterator for Source<'a> { self.current = self.current.and_then(Error::source); current } + + fn size_hint(&self) -> (usize, Option) { + if self.current.is_some() { (1, None) } else { (0, Some(0)) } + } } +#[unstable(feature = "error_iter", issue = "58520")] +impl<'a> crate::iter::FusedIterator for Source<'a> {} + #[stable(feature = "error_by_ref", since = "1.51.0")] impl<'a, T: Error + ?Sized> Error for &'a T { #[allow(deprecated, deprecated_in_future)] diff --git a/library/core/src/escape.rs b/library/core/src/escape.rs index f6ec30b9f793a..b213cc2b9167c 100644 --- a/library/core/src/escape.rs +++ b/library/core/src/escape.rs @@ -60,7 +60,7 @@ const fn escape_ascii(byte: u8) -> ([ascii::Char; N], Range) const fn escape_unicode(c: char) -> ([ascii::Char; N], Range) { const { assert!(N >= 10 && N < u8::MAX as usize) }; - let c = u32::from(c); + let c = c as u32; // OR-ing `1` ensures that for `c == 0` the code computes that // one digit should be printed. diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 297f52e756bc6..22084dcff8f88 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -3,16 +3,11 @@ use crate::cmp::Ordering; use crate::error::Error; use crate::ffi::c_char; -use crate::fmt; -use crate::intrinsics; use crate::iter::FusedIterator; use crate::marker::PhantomData; -use crate::ops; -use crate::ptr::addr_of; -use crate::ptr::NonNull; -use crate::slice; +use crate::ptr::{addr_of, NonNull}; use crate::slice::memchr; -use crate::str; +use crate::{fmt, intrinsics, ops, slice, str}; // FIXME: because this is doc(inline)d, we *have* to use intra-doc links because the actual link // depends on where the item is being documented. however, since this is libcore, we can't @@ -94,7 +89,7 @@ use crate::str; /// ``` /// /// [str]: prim@str "str" -#[derive(Hash)] +#[derive(PartialEq, Eq, Hash)] #[stable(feature = "core_c_str", since = "1.64.0")] #[rustc_has_incoherent_inherent_impls] #[lang = "CStr"] @@ -103,8 +98,7 @@ use crate::str; // However, `CStr` layout is considered an implementation detail and must not be relied upon. We // want `repr(transparent)` but we don't want it to show up in rustdoc, so we hide it under // `cfg(doc)`. This is an ad-hoc implementation of attribute privacy. -#[cfg_attr(not(doc), repr(transparent))] -#[allow(clippy::derived_hash_with_manual_eq)] +#[repr(transparent)] pub struct CStr { // FIXME: this should not be represented with a DST slice but rather with // just a raw `c_char` along with some form of marker to make @@ -263,8 +257,6 @@ impl CStr { /// ``` /// /// ``` - /// #![feature(const_cstr_from_ptr)] - /// /// use std::ffi::{c_char, CStr}; /// /// const HELLO_PTR: *const c_char = { @@ -280,11 +272,11 @@ impl CStr { #[inline] // inline is necessary for codegen to see strlen. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_cstr_from_ptr", issue = "113219")] + #[rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0")] pub const unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr { // SAFETY: The caller has provided a pointer that points to a valid C // string with a NUL terminator less than `isize::MAX` from `ptr`. - let len = unsafe { const_strlen(ptr) }; + let len = unsafe { strlen(ptr) }; // SAFETY: The caller has provided a valid pointer with length less than // `isize::MAX`, so `from_raw_parts` is safe. The content remains valid @@ -515,7 +507,10 @@ impl CStr { #[inline] #[must_use] const fn as_non_null_ptr(&self) -> NonNull { - NonNull::from(&self.inner).as_non_null_ptr() + // FIXME(effects) replace with `NonNull::from` + // SAFETY: a reference is never null + unsafe { NonNull::new_unchecked(&self.inner as *const [c_char] as *mut [c_char]) } + .as_non_null_ptr() } /// Returns the length of `self`. Like C's `strlen`, this does not include the nul terminator. @@ -539,7 +534,7 @@ impl CStr { #[must_use] #[doc(alias("len", "strlen"))] #[stable(feature = "cstr_count_bytes", since = "1.79.0")] - #[rustc_const_unstable(feature = "const_cstr_from_ptr", issue = "113219")] + #[rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0")] pub const fn count_bytes(&self) -> usize { self.inner.len() - 1 } @@ -677,15 +672,9 @@ impl CStr { } } -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for CStr { - #[inline] - fn eq(&self, other: &CStr) -> bool { - self.to_bytes().eq(other.to_bytes()) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for CStr {} +// `.to_bytes()` representations are compared instead of the inner `[c_char]`s, +// because `c_char` is `i8` (not `u8`) on some platforms. +// That is why this is implemented manually and not derived. #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for CStr { #[inline] @@ -739,7 +728,10 @@ impl AsRef for CStr { /// The pointer must point to a valid buffer that contains a NUL terminator. The NUL must be /// located within `isize::MAX` from `ptr`. #[inline] -const unsafe fn const_strlen(ptr: *const c_char) -> usize { +#[unstable(feature = "cstr_internals", issue = "none")] +#[rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0")] +#[rustc_allow_const_fn_unstable(const_eval_select)] +const unsafe fn strlen(ptr: *const c_char) -> usize { const fn strlen_ct(s: *const c_char) -> usize { let mut len = 0; @@ -777,8 +769,15 @@ const unsafe fn const_strlen(ptr: *const c_char) -> usize { pub struct Bytes<'a> { // since we know the string is nul-terminated, we only need one pointer ptr: NonNull, - phantom: PhantomData<&'a u8>, + phantom: PhantomData<&'a [c_char]>, } + +#[unstable(feature = "cstr_bytes", issue = "112115")] +unsafe impl Send for Bytes<'_> {} + +#[unstable(feature = "cstr_bytes", issue = "112115")] +unsafe impl Sync for Bytes<'_> {} + impl<'a> Bytes<'a> { #[inline] fn new(s: &'a CStr) -> Self { @@ -811,7 +810,7 @@ impl Iterator for Bytes<'_> { if ret == 0 { None } else { - self.ptr = self.ptr.offset(1); + self.ptr = self.ptr.add(1); Some(ret) } } @@ -821,6 +820,12 @@ impl Iterator for Bytes<'_> { fn size_hint(&self) -> (usize, Option) { if self.is_empty() { (0, Some(0)) } else { (1, None) } } + + #[inline] + fn count(self) -> usize { + // SAFETY: We always hold a valid pointer to a C string + unsafe { strlen(self.ptr.as_ptr().cast()) } + } } #[unstable(feature = "cstr_bytes", issue = "112115")] diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 27dacbb23d958..ec1f9052a1564 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -9,25 +9,34 @@ #![stable(feature = "core_ffi", since = "1.30.0")] #![allow(non_camel_case_types)] -use crate::fmt; -use crate::marker::PhantomData; -use crate::ops::{Deref, DerefMut}; - -#[doc(no_inline)] +#[doc(inline)] #[stable(feature = "core_c_str", since = "1.64.0")] -pub use self::c_str::FromBytesWithNulError; - +pub use self::c_str::CStr; #[doc(no_inline)] #[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] pub use self::c_str::FromBytesUntilNulError; - -#[doc(inline)] +#[doc(no_inline)] #[stable(feature = "core_c_str", since = "1.64.0")] -pub use self::c_str::CStr; +pub use self::c_str::FromBytesWithNulError; +use crate::fmt; #[unstable(feature = "c_str_module", issue = "112134")] pub mod c_str; +#[unstable( + feature = "c_variadic", + issue = "44930", + reason = "the `c_variadic` feature has not been properly tested on all supported platforms" +)] +pub use self::va_list::{VaList, VaListImpl}; + +#[unstable( + feature = "c_variadic", + issue = "44930", + reason = "the `c_variadic` feature has not been properly tested on all supported platforms" +)] +pub mod va_list; + macro_rules! type_alias { { $Docfile:tt, $Alias:ident = $Real:ty; @@ -133,7 +142,8 @@ mod c_char_definition { any(target_arch = "aarch64", target_arch = "riscv64") ), all(target_os = "nto", target_arch = "aarch64"), - target_os = "horizon" + target_os = "horizon", + target_os = "aix", ))] { pub type c_char = u8; } else { @@ -178,7 +188,7 @@ mod c_long_definition { // be UB. #[doc = include_str!("c_void.md")] #[lang = "c_void"] -#[cfg_attr(not(doc), repr(u8))] // work around https://github.com/rust-lang/rust/issues/90435 +#[cfg_attr(not(doc), repr(u8))] // An implementation detail we don't want to show up in rustdoc #[stable(feature = "core_c_void", since = "1.30.0")] pub enum c_void { #[unstable( @@ -204,403 +214,6 @@ impl fmt::Debug for c_void { } } -/// Basic implementation of a `va_list`. -// The name is WIP, using `VaListImpl` for now. -#[cfg(any( - all( - not(target_arch = "aarch64"), - not(target_arch = "powerpc"), - not(target_arch = "s390x"), - not(target_arch = "x86_64") - ), - all(target_arch = "aarch64", target_vendor = "apple"), - target_family = "wasm", - target_os = "uefi", - windows, -))] -#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - ptr: *mut c_void, - - // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to - // the region of the function it's defined in - _marker: PhantomData<&'f mut &'f c_void>, -} - -#[cfg(any( - all( - not(target_arch = "aarch64"), - not(target_arch = "powerpc"), - not(target_arch = "s390x"), - not(target_arch = "x86_64") - ), - all(target_arch = "aarch64", target_vendor = "apple"), - target_family = "wasm", - target_os = "uefi", - windows, -))] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> fmt::Debug for VaListImpl<'f> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "va_list* {:p}", self.ptr) - } -} - -/// AArch64 ABI implementation of a `va_list`. See the -/// [AArch64 Procedure Call Standard] for more details. -/// -/// [AArch64 Procedure Call Standard]: -/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf -#[cfg(all( - target_arch = "aarch64", - not(target_vendor = "apple"), - not(target_os = "uefi"), - not(windows), -))] -#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - stack: *mut c_void, - gr_top: *mut c_void, - vr_top: *mut c_void, - gr_offs: i32, - vr_offs: i32, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// PowerPC ABI implementation of a `va_list`. -#[cfg(all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)))] -#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - gpr: u8, - fpr: u8, - reserved: u16, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// s390x ABI implementation of a `va_list`. -#[cfg(target_arch = "s390x")] -#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - gpr: i64, - fpr: i64, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// x86_64 ABI implementation of a `va_list`. -#[cfg(all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)))] -#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - gp_offset: i32, - fp_offset: i32, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// A wrapper for a `va_list` -#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -pub struct VaList<'a, 'f: 'a> { - #[cfg(any( - all( - not(target_arch = "aarch64"), - not(target_arch = "powerpc"), - not(target_arch = "s390x"), - not(target_arch = "x86_64") - ), - all(target_arch = "aarch64", target_vendor = "apple"), - target_family = "wasm", - target_os = "uefi", - windows, - ))] - inner: VaListImpl<'f>, - - #[cfg(all( - any( - target_arch = "aarch64", - target_arch = "powerpc", - target_arch = "s390x", - target_arch = "x86_64" - ), - any(not(target_arch = "aarch64"), not(target_vendor = "apple")), - not(target_family = "wasm"), - not(target_os = "uefi"), - not(windows), - ))] - inner: &'a mut VaListImpl<'f>, - - _marker: PhantomData<&'a mut VaListImpl<'f>>, -} - -#[cfg(any( - all( - not(target_arch = "aarch64"), - not(target_arch = "powerpc"), - not(target_arch = "s390x"), - not(target_arch = "x86_64") - ), - all(target_arch = "aarch64", target_vendor = "apple"), - target_family = "wasm", - target_os = "uefi", - windows, -))] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> VaListImpl<'f> { - /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: VaListImpl { ..*self }, _marker: PhantomData } - } -} - -#[cfg(all( - any( - target_arch = "aarch64", - target_arch = "powerpc", - target_arch = "s390x", - target_arch = "x86_64" - ), - any(not(target_arch = "aarch64"), not(target_vendor = "apple")), - not(target_family = "wasm"), - not(target_os = "uefi"), - not(windows), -))] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> VaListImpl<'f> { - /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: self, _marker: PhantomData } - } -} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'a, 'f: 'a> Deref for VaList<'a, 'f> { - type Target = VaListImpl<'f>; - - #[inline] - fn deref(&self) -> &VaListImpl<'f> { - &self.inner - } -} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { - #[inline] - fn deref_mut(&mut self) -> &mut VaListImpl<'f> { - &mut self.inner - } -} - -// The VaArgSafe trait needs to be used in public interfaces, however, the trait -// itself must not be allowed to be used outside this module. Allowing users to -// implement the trait for a new type (thereby allowing the va_arg intrinsic to -// be used on a new type) is likely to cause undefined behavior. -// -// FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface -// but also ensure it cannot be used elsewhere, the trait needs to be public -// within a private module. Once RFC 2145 has been implemented look into -// improving this. -mod sealed_trait { - /// Trait which permits the allowed types to be used with [super::VaListImpl::arg]. - #[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" - )] - pub trait VaArgSafe {} -} - -macro_rules! impl_va_arg_safe { - ($($t:ty),+) => { - $( - #[unstable(feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930")] - impl sealed_trait::VaArgSafe for $t {} - )+ - } -} - -impl_va_arg_safe! {i8, i16, i32, i64, usize} -impl_va_arg_safe! {u8, u16, u32, u64, isize} -impl_va_arg_safe! {f64} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl sealed_trait::VaArgSafe for *mut T {} -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl sealed_trait::VaArgSafe for *const T {} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> VaListImpl<'f> { - /// Advance to the next arg. - #[inline] - pub unsafe fn arg(&mut self) -> T { - // SAFETY: the caller must uphold the safety contract for `va_arg`. - unsafe { va_arg(self) } - } - - /// Copies the `va_list` at the current location. - pub unsafe fn with_copy(&self, f: F) -> R - where - F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R, - { - let mut ap = self.clone(); - let ret = f(ap.as_va_list()); - // SAFETY: the caller must uphold the safety contract for `va_end`. - unsafe { - va_end(&mut ap); - } - ret - } -} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> Clone for VaListImpl<'f> { - #[inline] - fn clone(&self) -> Self { - let mut dest = crate::mem::MaybeUninit::uninit(); - // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal - unsafe { - va_copy(dest.as_mut_ptr(), self); - dest.assume_init() - } - } -} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> Drop for VaListImpl<'f> { - fn drop(&mut self) { - // FIXME: this should call `va_end`, but there's no clean way to - // guarantee that `drop` always gets inlined into its caller, - // so the `va_end` would get directly called from the same function as - // the corresponding `va_copy`. `man va_end` states that C requires this, - // and LLVM basically follows the C semantics, so we need to make sure - // that `va_end` is always called from the same function as `va_copy`. - // For more details, see https://github.com/rust-lang/rust/pull/59625 - // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic. - // - // This works for now, since `va_end` is a no-op on all current LLVM targets. - } -} - -extern "rust-intrinsic" { - /// Destroy the arglist `ap` after initialization with `va_start` or - /// `va_copy`. - #[rustc_nounwind] - fn va_end(ap: &mut VaListImpl<'_>); - - /// Copies the current location of arglist `src` to the arglist `dst`. - #[rustc_nounwind] - fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); - - /// Loads an argument of type `T` from the `va_list` `ap` and increment the - /// argument `ap` points to. - #[rustc_nounwind] - fn va_arg(ap: &mut VaListImpl<'_>) -> T; -} - // Link the MSVC default lib #[cfg(all(windows, target_env = "msvc"))] #[link( diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs new file mode 100644 index 0000000000000..3a224e4d8fe5f --- /dev/null +++ b/library/core/src/ffi/va_list.rs @@ -0,0 +1,300 @@ +//! C's "variable arguments" +//! +//! Better known as "varargs". + +use crate::ffi::c_void; +#[allow(unused_imports)] +use crate::fmt; +use crate::marker::PhantomData; +use crate::ops::{Deref, DerefMut}; + +/// Basic implementation of a `va_list`. +// The name is WIP, using `VaListImpl` for now. +#[cfg(any( + all( + not(target_arch = "aarch64"), + not(target_arch = "powerpc"), + not(target_arch = "s390x"), + not(target_arch = "x86_64") + ), + all(target_arch = "aarch64", target_vendor = "apple"), + target_family = "wasm", + target_os = "uefi", + windows, +))] +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + ptr: *mut c_void, + + // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to + // the region of the function it's defined in + _marker: PhantomData<&'f mut &'f c_void>, +} + +#[cfg(any( + all( + not(target_arch = "aarch64"), + not(target_arch = "powerpc"), + not(target_arch = "s390x"), + not(target_arch = "x86_64") + ), + all(target_arch = "aarch64", target_vendor = "apple"), + target_family = "wasm", + target_os = "uefi", + windows, +))] +impl<'f> fmt::Debug for VaListImpl<'f> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "va_list* {:p}", self.ptr) + } +} + +/// AArch64 ABI implementation of a `va_list`. See the +/// [AArch64 Procedure Call Standard] for more details. +/// +/// [AArch64 Procedure Call Standard]: +/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf +#[cfg(all( + target_arch = "aarch64", + not(target_vendor = "apple"), + not(target_os = "uefi"), + not(windows), +))] +#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 +#[derive(Debug)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + stack: *mut c_void, + gr_top: *mut c_void, + vr_top: *mut c_void, + gr_offs: i32, + vr_offs: i32, + _marker: PhantomData<&'f mut &'f c_void>, +} + +/// PowerPC ABI implementation of a `va_list`. +#[cfg(all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)))] +#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 +#[derive(Debug)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + gpr: u8, + fpr: u8, + reserved: u16, + overflow_arg_area: *mut c_void, + reg_save_area: *mut c_void, + _marker: PhantomData<&'f mut &'f c_void>, +} + +/// s390x ABI implementation of a `va_list`. +#[cfg(target_arch = "s390x")] +#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 +#[derive(Debug)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + gpr: i64, + fpr: i64, + overflow_arg_area: *mut c_void, + reg_save_area: *mut c_void, + _marker: PhantomData<&'f mut &'f c_void>, +} + +/// x86_64 ABI implementation of a `va_list`. +#[cfg(all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)))] +#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 +#[derive(Debug)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + gp_offset: i32, + fp_offset: i32, + overflow_arg_area: *mut c_void, + reg_save_area: *mut c_void, + _marker: PhantomData<&'f mut &'f c_void>, +} + +/// A wrapper for a `va_list` +#[repr(transparent)] +#[derive(Debug)] +pub struct VaList<'a, 'f: 'a> { + #[cfg(any( + all( + not(target_arch = "aarch64"), + not(target_arch = "powerpc"), + not(target_arch = "s390x"), + not(target_arch = "x86_64") + ), + all(target_arch = "aarch64", target_vendor = "apple"), + target_family = "wasm", + target_os = "uefi", + windows, + ))] + inner: VaListImpl<'f>, + + #[cfg(all( + any( + target_arch = "aarch64", + target_arch = "powerpc", + target_arch = "s390x", + target_arch = "x86_64" + ), + any(not(target_arch = "aarch64"), not(target_vendor = "apple")), + not(target_family = "wasm"), + not(target_os = "uefi"), + not(windows), + ))] + inner: &'a mut VaListImpl<'f>, + + _marker: PhantomData<&'a mut VaListImpl<'f>>, +} + +#[cfg(any( + all( + not(target_arch = "aarch64"), + not(target_arch = "powerpc"), + not(target_arch = "s390x"), + not(target_arch = "x86_64") + ), + all(target_arch = "aarch64", target_vendor = "apple"), + target_family = "wasm", + target_os = "uefi", + windows, +))] +impl<'f> VaListImpl<'f> { + /// Converts a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. + #[inline] + pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { + VaList { inner: VaListImpl { ..*self }, _marker: PhantomData } + } +} + +#[cfg(all( + any( + target_arch = "aarch64", + target_arch = "powerpc", + target_arch = "s390x", + target_arch = "x86_64" + ), + any(not(target_arch = "aarch64"), not(target_vendor = "apple")), + not(target_family = "wasm"), + not(target_os = "uefi"), + not(windows), +))] +impl<'f> VaListImpl<'f> { + /// Converts a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. + #[inline] + pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { + VaList { inner: self, _marker: PhantomData } + } +} + +impl<'a, 'f: 'a> Deref for VaList<'a, 'f> { + type Target = VaListImpl<'f>; + + #[inline] + fn deref(&self) -> &VaListImpl<'f> { + &self.inner + } +} + +impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { + #[inline] + fn deref_mut(&mut self) -> &mut VaListImpl<'f> { + &mut self.inner + } +} + +// The VaArgSafe trait needs to be used in public interfaces, however, the trait +// itself must not be allowed to be used outside this module. Allowing users to +// implement the trait for a new type (thereby allowing the va_arg intrinsic to +// be used on a new type) is likely to cause undefined behavior. +// +// FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface +// but also ensure it cannot be used elsewhere, the trait needs to be public +// within a private module. Once RFC 2145 has been implemented look into +// improving this. +mod sealed_trait { + /// Trait which permits the allowed types to be used with [super::VaListImpl::arg]. + pub unsafe trait VaArgSafe {} +} + +macro_rules! impl_va_arg_safe { + ($($t:ty),+) => { + $( + unsafe impl sealed_trait::VaArgSafe for $t {} + )+ + } +} + +impl_va_arg_safe! {i8, i16, i32, i64, usize} +impl_va_arg_safe! {u8, u16, u32, u64, isize} +impl_va_arg_safe! {f64} + +unsafe impl sealed_trait::VaArgSafe for *mut T {} +unsafe impl sealed_trait::VaArgSafe for *const T {} + +impl<'f> VaListImpl<'f> { + /// Advance to the next arg. + #[inline] + pub unsafe fn arg(&mut self) -> T { + // SAFETY: the caller must uphold the safety contract for `va_arg`. + unsafe { va_arg(self) } + } + + /// Copies the `va_list` at the current location. + pub unsafe fn with_copy(&self, f: F) -> R + where + F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R, + { + let mut ap = self.clone(); + let ret = f(ap.as_va_list()); + // SAFETY: the caller must uphold the safety contract for `va_end`. + unsafe { + va_end(&mut ap); + } + ret + } +} + +impl<'f> Clone for VaListImpl<'f> { + #[inline] + fn clone(&self) -> Self { + let mut dest = crate::mem::MaybeUninit::uninit(); + // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal + unsafe { + va_copy(dest.as_mut_ptr(), self); + dest.assume_init() + } + } +} + +impl<'f> Drop for VaListImpl<'f> { + fn drop(&mut self) { + // FIXME: this should call `va_end`, but there's no clean way to + // guarantee that `drop` always gets inlined into its caller, + // so the `va_end` would get directly called from the same function as + // the corresponding `va_copy`. `man va_end` states that C requires this, + // and LLVM basically follows the C semantics, so we need to make sure + // that `va_end` is always called from the same function as `va_copy`. + // For more details, see https://github.com/rust-lang/rust/pull/59625 + // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic. + // + // This works for now, since `va_end` is a no-op on all current LLVM targets. + } +} + +extern "rust-intrinsic" { + /// Destroy the arglist `ap` after initialization with `va_start` or + /// `va_copy`. + #[rustc_nounwind] + fn va_end(ap: &mut VaListImpl<'_>); + + /// Copies the current location of arglist `src` to the arglist `dst`. + #[rustc_nounwind] + fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); + + /// Loads an argument of type `T` from the `va_list` `ap` and increment the + /// argument `ap` points to. + #[rustc_nounwind] + fn va_arg(ap: &mut VaListImpl<'_>) -> T; +} diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 4ccb585862cdf..794ca1851b13d 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -402,6 +402,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { } } +/// A helper used to print list-like items with no special formatting. struct DebugInner<'a, 'b: 'a> { fmt: &'a mut fmt::Formatter<'b>, result: fmt::Result, @@ -578,7 +579,8 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn finish(&mut self) -> fmt::Result { - self.inner.result.and_then(|_| self.inner.fmt.write_str("}")) + self.inner.result = self.inner.result.and_then(|_| self.inner.fmt.write_str("}")); + self.inner.result } } @@ -721,7 +723,8 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn finish(&mut self) -> fmt::Result { - self.inner.result.and_then(|_| self.inner.fmt.write_str("]")) + self.inner.result = self.inner.result.and_then(|_| self.inner.fmt.write_str("]")); + self.inner.result } } @@ -1002,11 +1005,12 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn finish(&mut self) -> fmt::Result { - self.result.and_then(|_| { + self.result = self.result.and_then(|_| { assert!(!self.has_key, "attempted to finish a map with a partial entry"); self.fmt.write_str("}") - }) + }); + self.result } fn is_pretty(&self) -> bool { @@ -1026,7 +1030,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// assert_eq!(format!("{}", value), "a"); /// assert_eq!(format!("{:?}", value), "'a'"); /// -/// let wrapped = fmt::FormatterFn(|f| write!(f, "{:?}", &value)); +/// let wrapped = fmt::FormatterFn(|f| write!(f, "{value:?}")); /// assert_eq!(format!("{}", wrapped), "'a'"); /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 7f23d3c09567c..20ea0352c2dce 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -1,7 +1,6 @@ use crate::fmt::{Debug, Display, Formatter, LowerExp, Result, UpperExp}; use crate::mem::MaybeUninit; -use crate::num::flt2dec; -use crate::num::fmt as numfmt; +use crate::num::{flt2dec, fmt as numfmt}; #[doc(hidden)] trait GeneralFormat: PartialOrd { @@ -35,8 +34,8 @@ fn float_to_decimal_common_exact( where T: flt2dec::DecodableFloat, { - let mut buf: [MaybeUninit; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64 - let mut parts: [MaybeUninit>; 4] = MaybeUninit::uninit_array(); + let mut buf: [MaybeUninit; 1024] = [MaybeUninit::uninit(); 1024]; // enough for f32 and f64 + let mut parts: [MaybeUninit>; 4] = [MaybeUninit::uninit(); 4]; let formatted = flt2dec::to_exact_fixed_str( flt2dec::strategy::grisu::format_exact, *num, @@ -62,8 +61,9 @@ where T: flt2dec::DecodableFloat, { // enough for f32 and f64 - let mut buf: [MaybeUninit; flt2dec::MAX_SIG_DIGITS] = MaybeUninit::uninit_array(); - let mut parts: [MaybeUninit>; 4] = MaybeUninit::uninit_array(); + let mut buf: [MaybeUninit; flt2dec::MAX_SIG_DIGITS] = + [MaybeUninit::uninit(); flt2dec::MAX_SIG_DIGITS]; + let mut parts: [MaybeUninit>; 4] = [MaybeUninit::uninit(); 4]; let formatted = flt2dec::to_shortest_str( flt2dec::strategy::grisu::format_shortest, *num, @@ -107,8 +107,8 @@ fn float_to_exponential_common_exact( where T: flt2dec::DecodableFloat, { - let mut buf: [MaybeUninit; 1024] = MaybeUninit::uninit_array(); // enough for f32 and f64 - let mut parts: [MaybeUninit>; 6] = MaybeUninit::uninit_array(); + let mut buf: [MaybeUninit; 1024] = [MaybeUninit::uninit(); 1024]; // enough for f32 and f64 + let mut parts: [MaybeUninit>; 6] = [MaybeUninit::uninit(); 6]; let formatted = flt2dec::to_exact_exp_str( flt2dec::strategy::grisu::format_exact, *num, @@ -135,8 +135,9 @@ where T: flt2dec::DecodableFloat, { // enough for f32 and f64 - let mut buf: [MaybeUninit; flt2dec::MAX_SIG_DIGITS] = MaybeUninit::uninit_array(); - let mut parts: [MaybeUninit>; 6] = MaybeUninit::uninit_array(); + let mut buf: [MaybeUninit; flt2dec::MAX_SIG_DIGITS] = + [MaybeUninit::uninit(); flt2dec::MAX_SIG_DIGITS]; + let mut parts: [MaybeUninit>; 6] = [MaybeUninit::uninit(); 6]; let formatted = flt2dec::to_shortest_exp_str( flt2dec::strategy::grisu::format_shortest, *num, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 1324fb6e056be..60c0dc7685253 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -4,13 +4,10 @@ use crate::cell::{Cell, Ref, RefCell, RefMut, SyncUnsafeCell, UnsafeCell}; use crate::char::EscapeDebugExtArgs; -use crate::iter; use crate::marker::PhantomData; -use crate::mem; use crate::num::fmt as numfmt; use crate::ops::Deref; -use crate::result; -use crate::str; +use crate::{iter, mem, result, str}; mod builders; #[cfg(not(no_fp_fmt_parse))] @@ -36,11 +33,10 @@ pub enum Alignment { Center, } -#[stable(feature = "debug_builders", since = "1.2.0")] -pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; - #[unstable(feature = "debug_closure_helpers", issue = "117729")] pub use self::builders::FormatterFn; +#[stable(feature = "debug_builders", since = "1.2.0")] +pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; /// The type returned by formatter methods. /// @@ -338,27 +334,23 @@ pub struct Arguments<'a> { impl<'a> Arguments<'a> { #[inline] #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] - pub const fn new_const(pieces: &'a [&'static str]) -> Self { - if pieces.len() > 1 { - // Since panic!() expands to panic_fmt(format_args!()), using panic! here is both a - // bit silly and also significantly increases the amount of MIR generated by panics. - crate::panicking::panic_nounwind("invalid args"); - } + pub const fn new_const(pieces: &'a [&'static str; N]) -> Self { + const { assert!(N <= 1) }; Arguments { pieces, fmt: None, args: &[] } } /// When using the format_args!() macro, this function is used to generate the /// Arguments structure. #[inline] - pub fn new_v1(pieces: &'a [&'static str], args: &'a [rt::Argument<'a>]) -> Arguments<'a> { - if pieces.len() < args.len() || pieces.len() > args.len() + 1 { - // See Arguments::new_const for why we don't use panic!. - crate::panicking::panic_nounwind("invalid args"); - } + pub fn new_v1( + pieces: &'a [&'static str; P], + args: &'a [rt::Argument<'a>; A], + ) -> Arguments<'a> { + const { assert!(P >= A && P <= A + 1, "invalid args") } Arguments { pieces, fmt: None, args } } - /// This function is used to specify nonstandard formatting parameters. + /// Specifies nonstandard formatting parameters. /// /// An `rt::UnsafeArg` is required because the following invariants must be held /// in order for this function to be safe: @@ -400,7 +392,7 @@ impl<'a> Arguments<'a> { } impl<'a> Arguments<'a> { - /// Get the formatted string, if it has no arguments to be formatted at runtime. + /// Gets the formatted string, if it has no arguments to be formatted at runtime. /// /// This can be used to avoid allocations in some cases. /// @@ -463,6 +455,12 @@ impl<'a> Arguments<'a> { } } +// Manually implementing these results in better error messages. +#[stable(feature = "rust1", since = "1.0.0")] +impl !Send for Arguments<'_> {} +#[stable(feature = "rust1", since = "1.0.0")] +impl !Sync for Arguments<'_> {} + #[stable(feature = "rust1", since = "1.0.0")] impl Debug for Arguments<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { @@ -515,7 +513,10 @@ impl Display for Arguments<'_> { /// /// let origin = Point { x: 0, y: 0 }; /// -/// assert_eq!(format!("The origin is: {origin:?}"), "The origin is: Point { x: 0, y: 0 }"); +/// assert_eq!( +/// format!("The origin is: {origin:?}"), +/// "The origin is: Point { x: 0, y: 0 }", +/// ); /// ``` /// /// Manually implementing: @@ -539,7 +540,10 @@ impl Display for Arguments<'_> { /// /// let origin = Point { x: 0, y: 0 }; /// -/// assert_eq!(format!("The origin is: {origin:?}"), "The origin is: Point { x: 0, y: 0 }"); +/// assert_eq!( +/// format!("The origin is: {origin:?}"), +/// "The origin is: Point { x: 0, y: 0 }", +/// ); /// ``` /// /// There are a number of helper methods on the [`Formatter`] struct to help you with manual @@ -580,11 +584,11 @@ impl Display for Arguments<'_> { /// /// let origin = Point { x: 0, y: 0 }; /// -/// assert_eq!(format!("The origin is: {origin:#?}"), -/// "The origin is: Point { +/// let expected = "The origin is: Point { /// x: 0, /// y: 0, -/// }"); +/// }"; +/// assert_eq!(format!("The origin is: {origin:#?}"), expected); /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -736,8 +740,10 @@ pub trait Display { /// } /// } /// - /// assert_eq!("(1.987, 2.983)", - /// format!("{}", Position { longitude: 1.987, latitude: 2.983, })); + /// assert_eq!( + /// "(1.987, 2.983)", + /// format!("{}", Position { longitude: 1.987, latitude: 2.983, }), + /// ); /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn fmt(&self, f: &mut Formatter<'_>) -> Result; @@ -1119,8 +1125,8 @@ pub trait UpperExp { fn fmt(&self, f: &mut Formatter<'_>) -> Result; } -/// The `write` function takes an output stream, and an `Arguments` struct -/// that can be precompiled with the `format_args!` macro. +/// Takes an output stream and an `Arguments` struct that can be precompiled with +/// the `format_args!` macro. /// /// The arguments will be formatted according to the specified format string /// into the output stream provided. @@ -1247,7 +1253,7 @@ impl PostPadding { PostPadding { fill, padding } } - /// Write this post padding. + /// Writes this post padding. pub(crate) fn write(self, f: &mut Formatter<'_>) -> Result { for _ in 0..self.padding { f.buf.write_char(self.fill)?; @@ -1388,9 +1394,10 @@ impl<'a> Formatter<'a> { } } - /// This function takes a string slice and emits it to the internal buffer - /// after applying the relevant formatting flags specified. The flags - /// recognized for generic strings are: + /// Takes a string slice and emits it to the internal buffer after applying + /// the relevant formatting flags specified. + /// + /// The flags recognized for generic strings are: /// /// * width - the minimum width of what to emit /// * fill/align - what to emit and where to emit it if the string @@ -1464,9 +1471,10 @@ impl<'a> Formatter<'a> { } } - /// Write the pre-padding and return the unwritten post-padding. Callers are - /// responsible for ensuring post-padding is written after the thing that is - /// being padded. + /// Writes the pre-padding and returns the unwritten post-padding. + /// + /// Callers are responsible for ensuring post-padding is written after the + /// thing that is being padded. pub(crate) fn padding( &mut self, padding: usize, @@ -1493,6 +1501,7 @@ impl<'a> Formatter<'a> { } /// Takes the formatted parts and applies the padding. + /// /// Assumes that the caller already has rendered the parts with required precision, /// so that `self.precision` can be ignored. /// @@ -1645,7 +1654,7 @@ impl<'a> Formatter<'a> { } } - /// Flags for formatting + /// Returns flags for formatting. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[deprecated( @@ -1657,7 +1666,7 @@ impl<'a> Formatter<'a> { self.flags } - /// Character used as 'fill' whenever there is alignment. + /// Returns the character used as 'fill' whenever there is alignment. /// /// # Examples /// @@ -1690,7 +1699,7 @@ impl<'a> Formatter<'a> { self.fill } - /// Flag indicating what form of alignment was requested. + /// Returns a flag indicating what form of alignment was requested. /// /// # Examples /// @@ -1730,7 +1739,7 @@ impl<'a> Formatter<'a> { } } - /// Optionally specified integer width that the output should be. + /// Returns the optionally specified integer width that the output should be. /// /// # Examples /// @@ -1760,8 +1769,8 @@ impl<'a> Formatter<'a> { self.width } - /// Optionally specified precision for numeric types. Alternatively, the - /// maximum width for string types. + /// Returns the optionally specified precision for numeric types. + /// Alternatively, the maximum width for string types. /// /// # Examples /// @@ -1957,8 +1966,9 @@ impl<'a> Formatter<'a> { builders::debug_struct_new(self, name) } - /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. - /// `debug_struct_fields_finish` is more general, but this is faster for 1 field. + /// Shrinks `derive(Debug)` code, for faster compilation and smaller + /// binaries. `debug_struct_fields_finish` is more general, but this is + /// faster for 1 field. #[doc(hidden)] #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] pub fn debug_struct_field1_finish<'b>( @@ -1972,8 +1982,9 @@ impl<'a> Formatter<'a> { builder.finish() } - /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. - /// `debug_struct_fields_finish` is more general, but this is faster for 2 fields. + /// Shrinks `derive(Debug)` code, for faster compilation and smaller + /// binaries. `debug_struct_fields_finish` is more general, but this is + /// faster for 2 fields. #[doc(hidden)] #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] pub fn debug_struct_field2_finish<'b>( @@ -1990,8 +2001,9 @@ impl<'a> Formatter<'a> { builder.finish() } - /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. - /// `debug_struct_fields_finish` is more general, but this is faster for 3 fields. + /// Shrinks `derive(Debug)` code, for faster compilation and smaller + /// binaries. `debug_struct_fields_finish` is more general, but this is + /// faster for 3 fields. #[doc(hidden)] #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] pub fn debug_struct_field3_finish<'b>( @@ -2011,8 +2023,9 @@ impl<'a> Formatter<'a> { builder.finish() } - /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. - /// `debug_struct_fields_finish` is more general, but this is faster for 4 fields. + /// Shrinks `derive(Debug)` code, for faster compilation and smaller + /// binaries. `debug_struct_fields_finish` is more general, but this is + /// faster for 4 fields. #[doc(hidden)] #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] pub fn debug_struct_field4_finish<'b>( @@ -2035,8 +2048,9 @@ impl<'a> Formatter<'a> { builder.finish() } - /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. - /// `debug_struct_fields_finish` is more general, but this is faster for 5 fields. + /// Shrinks `derive(Debug)` code, for faster compilation and smaller + /// binaries. `debug_struct_fields_finish` is more general, but this is + /// faster for 5 fields. #[doc(hidden)] #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] pub fn debug_struct_field5_finish<'b>( @@ -2062,7 +2076,7 @@ impl<'a> Formatter<'a> { builder.finish() } - /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. + /// Shrinks `derive(Debug)` code, for faster compilation and smaller binaries. /// For the cases not covered by `debug_struct_field[12345]_finish`. #[doc(hidden)] #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] @@ -2111,8 +2125,9 @@ impl<'a> Formatter<'a> { builders::debug_tuple_new(self, name) } - /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. - /// `debug_tuple_fields_finish` is more general, but this is faster for 1 field. + /// Shrinks `derive(Debug)` code, for faster compilation and smaller + /// binaries. `debug_tuple_fields_finish` is more general, but this is faster + /// for 1 field. #[doc(hidden)] #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] pub fn debug_tuple_field1_finish<'b>(&'b mut self, name: &str, value1: &dyn Debug) -> Result { @@ -2121,8 +2136,9 @@ impl<'a> Formatter<'a> { builder.finish() } - /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. - /// `debug_tuple_fields_finish` is more general, but this is faster for 2 fields. + /// Shrinks `derive(Debug)` code, for faster compilation and smaller + /// binaries. `debug_tuple_fields_finish` is more general, but this is faster + /// for 2 fields. #[doc(hidden)] #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] pub fn debug_tuple_field2_finish<'b>( @@ -2137,8 +2153,9 @@ impl<'a> Formatter<'a> { builder.finish() } - /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. - /// `debug_tuple_fields_finish` is more general, but this is faster for 3 fields. + /// Shrinks `derive(Debug)` code, for faster compilation and smaller + /// binaries. `debug_tuple_fields_finish` is more general, but this is faster + /// for 3 fields. #[doc(hidden)] #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] pub fn debug_tuple_field3_finish<'b>( @@ -2155,8 +2172,9 @@ impl<'a> Formatter<'a> { builder.finish() } - /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. - /// `debug_tuple_fields_finish` is more general, but this is faster for 4 fields. + /// Shrinks `derive(Debug)` code, for faster compilation and smaller + /// binaries. `debug_tuple_fields_finish` is more general, but this is faster + /// for 4 fields. #[doc(hidden)] #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] pub fn debug_tuple_field4_finish<'b>( @@ -2175,8 +2193,9 @@ impl<'a> Formatter<'a> { builder.finish() } - /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. - /// `debug_tuple_fields_finish` is more general, but this is faster for 5 fields. + /// Shrinks `derive(Debug)` code, for faster compilation and smaller + /// binaries. `debug_tuple_fields_finish` is more general, but this is faster + /// for 5 fields. #[doc(hidden)] #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] pub fn debug_tuple_field5_finish<'b>( @@ -2197,8 +2216,8 @@ impl<'a> Formatter<'a> { builder.finish() } - /// Used to shrink `derive(Debug)` code, for faster compilation and smaller binaries. - /// For the cases not covered by `debug_tuple_field[12345]_finish`. + /// Shrinks `derive(Debug)` code, for faster compilation and smaller + /// binaries. For the cases not covered by `debug_tuple_field[12345]_finish`. #[doc(hidden)] #[unstable(feature = "fmt_helpers_for_derive", issue = "none")] pub fn debug_tuple_fields_finish<'b>( @@ -2482,13 +2501,13 @@ impl Display for char { #[stable(feature = "rust1", since = "1.0.0")] impl Pointer for *const T { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - // Cast is needed here because `.expose_provenance()` requires `T: Sized`. - pointer_fmt_inner((*self as *const ()).expose_provenance(), f) + pointer_fmt_inner(self.expose_provenance(), f) } } -/// Since the formatting will be identical for all pointer types, use a non-monomorphized -/// implementation for the actual formatting to reduce the amount of codegen work needed. +/// Since the formatting will be identical for all pointer types, uses a +/// non-monomorphized implementation for the actual formatting to reduce the +/// amount of codegen work needed. /// /// This uses `ptr_addr: usize` and not `ptr: *const ()` to be able to use this for /// `fn(...) -> ...` without using [problematic] "Oxford Casts". diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index ab2158394bf1e..e7726da8d91f2 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -1,12 +1,9 @@ //! Integer and floating-point number formatting -use crate::fmt; use crate::mem::MaybeUninit; use crate::num::fmt as numfmt; use crate::ops::{Div, Rem, Sub}; -use crate::ptr; -use crate::slice; -use crate::str; +use crate::{fmt, ptr, slice, str}; #[doc(hidden)] trait DisplayInt: @@ -212,6 +209,7 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ macro_rules! impl_Display { ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => { + #[cfg(not(feature = "optimize_for_size"))] fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { // 2^128 is about 3*10^38, so 39 gives an extra byte of space let mut buf = [MaybeUninit::::uninit(); 39]; @@ -277,6 +275,38 @@ macro_rules! impl_Display { f.pad_integral(is_nonnegative, "", buf_slice) } + #[cfg(feature = "optimize_for_size")] + fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 2^128 is about 3*10^38, so 39 gives an extra byte of space + let mut buf = [MaybeUninit::::uninit(); 39]; + let mut curr = buf.len(); + let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); + + // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning + // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at + // each step this is kept the same as `n` is divided. Since `n` is always + // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` + // is safe to access. + unsafe { + loop { + curr -= 1; + buf_ptr.add(curr).write((n % 10) as u8 + b'0'); + n /= 10; + + if n == 0 { + break; + } + } + } + + // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8 + let buf_slice = unsafe { + str::from_utf8_unchecked( + slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) + }; + f.pad_integral(is_nonnegative, "", buf_slice) + } + $(#[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for $t { #[allow(unused_comparisons)] diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs index 92626feabf3d7..65a4d537cc74d 100644 --- a/library/core/src/fmt/rt.rs +++ b/library/core/src/fmt/rt.rs @@ -5,6 +5,7 @@ use super::*; use crate::hint::unreachable_unchecked; +use crate::ptr::NonNull; #[lang = "format_placeholder"] #[derive(Copy, Clone)] @@ -66,7 +67,13 @@ pub(super) enum Flag { #[derive(Copy, Clone)] enum ArgumentType<'a> { - Placeholder { value: &'a Opaque, formatter: fn(&Opaque, &mut Formatter<'_>) -> Result }, + Placeholder { + // INVARIANT: `formatter` has type `fn(&T, _) -> _` for some `T`, and `value` + // was derived from a `&'a T`. + value: NonNull<()>, + formatter: unsafe fn(NonNull<()>, &mut Formatter<'_>) -> Result, + _lifetime: PhantomData<&'a ()>, + }, Count(usize), } @@ -90,21 +97,15 @@ pub struct Argument<'a> { impl<'a> Argument<'a> { #[inline(always)] fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> { - // SAFETY: `mem::transmute(x)` is safe because - // 1. `&'b T` keeps the lifetime it originated with `'b` - // (so as to not have an unbounded lifetime) - // 2. `&'b T` and `&'b Opaque` have the same memory layout - // (when `T` is `Sized`, as it is here) - // `mem::transmute(f)` is safe since `fn(&T, &mut Formatter<'_>) -> Result` - // and `fn(&Opaque, &mut Formatter<'_>) -> Result` have the same ABI - // (as long as `T` is `Sized`) - unsafe { - Argument { - ty: ArgumentType::Placeholder { - formatter: mem::transmute(f), - value: mem::transmute(x), - }, - } + Argument { + // INVARIANT: this creates an `ArgumentType<'b>` from a `&'b T` and + // a `fn(&T, ...)`, so the invariant is maintained. + ty: ArgumentType::Placeholder { + value: NonNull::from(x).cast(), + // SAFETY: function pointers always have the same layout. + formatter: unsafe { mem::transmute(f) }, + _lifetime: PhantomData, + }, } } @@ -162,7 +163,14 @@ impl<'a> Argument<'a> { #[inline(always)] pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result { match self.ty { - ArgumentType::Placeholder { formatter, value } => formatter(value, f), + // SAFETY: + // Because of the invariant that if `formatter` had the type + // `fn(&T, _) -> _` then `value` has type `&'b T` where `'b` is + // the lifetime of the `ArgumentType`, and because references + // and `NonNull` are ABI-compatible, this is completely equivalent + // to calling the original function passed to `new` with the + // original reference, which is sound. + ArgumentType::Placeholder { formatter, value, .. } => unsafe { formatter(value, f) }, // SAFETY: the caller promised this. ArgumentType::Count(_) => unsafe { unreachable_unchecked() }, } @@ -208,7 +216,3 @@ impl UnsafeArg { Self { _private: () } } } - -extern "C" { - type Opaque; -} diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs index 0eb8d7bb32899..8971a2c0aafd1 100644 --- a/library/core/src/future/async_drop.rs +++ b/library/core/src/future/async_drop.rs @@ -1,4 +1,4 @@ -#![unstable(feature = "async_drop", issue = "none")] +#![unstable(feature = "async_drop", issue = "126482")] use crate::fmt; use crate::future::{Future, IntoFuture}; @@ -10,27 +10,27 @@ use crate::task::{ready, Context, Poll}; /// Asynchronously drops a value by running `AsyncDrop::async_drop` /// on a value and its fields recursively. -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] pub fn async_drop(value: T) -> AsyncDropOwning { AsyncDropOwning { value: MaybeUninit::new(value), dtor: None, _pinned: PhantomPinned } } /// A future returned by the [`async_drop`]. -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] pub struct AsyncDropOwning { value: MaybeUninit, dtor: Option>, _pinned: PhantomPinned, } -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] impl fmt::Debug for AsyncDropOwning { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AsyncDropOwning").finish_non_exhaustive() } } -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] impl Future for AsyncDropOwning { type Output = (); @@ -86,24 +86,24 @@ unsafe fn async_drop_in_place_raw( /// returned future stores the `to_drop` pointer and user is required /// to guarantee that dropped value doesn't move. /// -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] pub unsafe fn async_drop_in_place(to_drop: *mut T) -> AsyncDropInPlace { // SAFETY: `async_drop_in_place_raw` has the same safety requirements unsafe { AsyncDropInPlace(async_drop_in_place_raw(to_drop)) } } /// A future returned by the [`async_drop_in_place`]. -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] pub struct AsyncDropInPlace(::AsyncDestructor); -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] impl fmt::Debug for AsyncDropInPlace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AsyncDropInPlace").finish_non_exhaustive() } } -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] impl Future for AsyncDropInPlace { type Output = (); @@ -117,18 +117,18 @@ impl Future for AsyncDropInPlace { // FIXME(zetanumbers): Add same restrictions on AsyncDrop impls as // with Drop impls /// Custom code within the asynchronous destructor. -#[unstable(feature = "async_drop", issue = "none")] +#[unstable(feature = "async_drop", issue = "126482")] #[lang = "async_drop"] pub trait AsyncDrop { /// A future returned by the [`AsyncDrop::async_drop`] to be part /// of the async destructor. - #[unstable(feature = "async_drop", issue = "none")] + #[unstable(feature = "async_drop", issue = "126482")] type Dropper<'a>: Future where Self: 'a; /// Constructs the asynchronous destructor for this type. - #[unstable(feature = "async_drop", issue = "none")] + #[unstable(feature = "async_drop", issue = "126482")] fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_>; } @@ -161,6 +161,11 @@ async unsafe fn surface_drop_in_place(ptr: *mut T) { /// wrapped future completes by returning `Poll::Ready(())` on poll. This /// is useful for constructing async destructors to guarantee this /// "fuse" property +// +// FIXME: Consider optimizing combinators to not have to use fuse in majority +// of cases, perhaps by adding `#[(rustc_)idempotent(_future)]` attribute for +// async functions and blocks with the unit return type. However current layout +// optimizations currently encode `None` case into the async block's discriminant. struct Fuse { inner: Option, } @@ -200,7 +205,7 @@ async unsafe fn slice(s: *mut [T]) { } } -/// Construct a chain of two futures, which awaits them sequentially as +/// Constructs a chain of two futures, which awaits them sequentially as /// a future. #[lang = "async_drop_chain"] async fn chain(first: F, last: G) @@ -230,8 +235,8 @@ async unsafe fn defer(to_drop: *mut T) { /// /// # Safety /// -/// User should carefully manage returned future, since it would -/// try creating an immutable referece from `this` and get pointee's +/// Users should carefully manage the returned future, since it would +/// try creating an immutable reference from `this` and get pointee's /// discriminant. // FIXME(zetanumbers): Send and Sync impls #[lang = "async_drop_either"] @@ -251,6 +256,13 @@ async unsafe fn either, M: IntoFuture, T } } +#[lang = "async_drop_deferred_drop_in_place"] +async unsafe fn deferred_drop_in_place(to_drop: *mut T) { + // SAFETY: same safety requirements as with drop_in_place (implied by + // function's name) + unsafe { crate::ptr::drop_in_place(to_drop) } +} + /// Used for noop async destructors. We don't use [`core::future::Ready`] /// because it panics after its second poll, which could be potentially /// bad if that would happen during the cleanup. diff --git a/library/core/src/future/future.rs b/library/core/src/future/future.rs index f965afc8a5937..ca1c2d1ca1f2e 100644 --- a/library/core/src/future/future.rs +++ b/library/core/src/future/future.rs @@ -35,10 +35,10 @@ use crate::task::{Context, Poll}; pub trait Future { /// The type of value produced on completion. #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_diagnostic_item = "FutureOutput"] + #[lang = "future_output"] type Output; - /// Attempt to resolve the future to a final value, registering + /// Attempts to resolve the future to a final value, registering /// the current task for wakeup if the value is not yet available. /// /// # Return value diff --git a/library/core/src/future/into_future.rs b/library/core/src/future/into_future.rs index eb5a9b72dd0f2..aa546b940b23a 100644 --- a/library/core/src/future/into_future.rs +++ b/library/core/src/future/into_future.rs @@ -40,7 +40,7 @@ use crate::future::Future; /// } /// /// impl Multiply { -/// /// Construct a new instance of `Multiply`. +/// /// Constructs a new instance of `Multiply`. /// pub fn new(num: u16, factor: u16) -> Self { /// Self { num, factor } /// } @@ -89,7 +89,7 @@ use crate::future::Future; /// ```rust /// use std::future::IntoFuture; /// -/// /// Convert the output of a future to a string. +/// /// Converts the output of a future to a string. /// async fn fut_to_string(fut: Fut) -> String /// where /// Fut: IntoFuture, diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs index 873cccc7e96fd..d188f1c713079 100644 --- a/library/core/src/future/mod.rs +++ b/library/core/src/future/mod.rs @@ -20,25 +20,21 @@ mod pending; mod poll_fn; mod ready; -#[stable(feature = "futures_api", since = "1.36.0")] -pub use self::future::Future; - -#[unstable(feature = "future_join", issue = "91642")] -pub use self::join::join; - +#[unstable(feature = "async_drop", issue = "126482")] +pub use async_drop::{async_drop, async_drop_in_place, AsyncDrop, AsyncDropInPlace}; #[stable(feature = "into_future", since = "1.64.0")] pub use into_future::IntoFuture; - #[stable(feature = "future_readiness_fns", since = "1.48.0")] pub use pending::{pending, Pending}; -#[stable(feature = "future_readiness_fns", since = "1.48.0")] -pub use ready::{ready, Ready}; - #[stable(feature = "future_poll_fn", since = "1.64.0")] pub use poll_fn::{poll_fn, PollFn}; +#[stable(feature = "future_readiness_fns", since = "1.48.0")] +pub use ready::{ready, Ready}; -#[unstable(feature = "async_drop", issue = "none")] -pub use async_drop::{async_drop, async_drop_in_place, AsyncDrop, AsyncDropInPlace}; +#[stable(feature = "futures_api", since = "1.36.0")] +pub use self::future::Future; +#[unstable(feature = "future_join", issue = "91642")] +pub use self::join::join; /// This type is needed because: /// diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index 1c93a7b28fd35..061690e88ddf8 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -83,17 +83,14 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::fmt; -use crate::marker; - #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated)] pub use self::sip::SipHasher; - #[unstable(feature = "hashmap_internals", issue = "none")] #[allow(deprecated)] #[doc(hidden)] pub use self::sip::SipHasher13; +use crate::{fmt, marker}; mod sip; @@ -334,6 +331,7 @@ pub trait Hasher { /// /// [`write`]: Hasher::write #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] fn finish(&self) -> u64; /// Writes some data into this `Hasher`. @@ -805,10 +803,8 @@ impl PartialEq for BuildHasherDefault { impl Eq for BuildHasherDefault {} mod impls { - use crate::mem; - use crate::slice; - use super::*; + use crate::{mem, slice}; macro_rules! impl_write { ($(($ty:ident, $meth:ident),)*) => {$( diff --git a/library/core/src/hash/sip.rs b/library/core/src/hash/sip.rs index 78a232faaf88c..17f2caaa0c083 100644 --- a/library/core/src/hash/sip.rs +++ b/library/core/src/hash/sip.rs @@ -2,10 +2,8 @@ #![allow(deprecated)] // the types in this module are deprecated -use crate::cmp; use crate::marker::PhantomData; -use crate::mem; -use crate::ptr; +use crate::{cmp, mem, ptr}; /// An implementation of SipHash 1-3. /// @@ -76,18 +74,19 @@ macro_rules! compress { ($state:expr) => {{ compress!($state.v0, $state.v1, $state.v2, $state.v3) }}; ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => {{ $v0 = $v0.wrapping_add($v1); + $v2 = $v2.wrapping_add($v3); $v1 = $v1.rotate_left(13); $v1 ^= $v0; - $v0 = $v0.rotate_left(32); - $v2 = $v2.wrapping_add($v3); $v3 = $v3.rotate_left(16); $v3 ^= $v2; - $v0 = $v0.wrapping_add($v3); - $v3 = $v3.rotate_left(21); - $v3 ^= $v0; + $v0 = $v0.rotate_left(32); + $v2 = $v2.wrapping_add($v1); + $v0 = $v0.wrapping_add($v3); $v1 = $v1.rotate_left(17); $v1 ^= $v2; + $v3 = $v3.rotate_left(21); + $v3 ^= $v0; $v2 = $v2.rotate_left(32); }}; } diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 6e2d88c6b8337..6ca5e53df3b01 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -3,8 +3,7 @@ //! Hints to compiler that affects how code should be emitted or optimized. //! Hints may be compile time or runtime. -use crate::intrinsics; -use crate::ub_checks; +use crate::{intrinsics, ub_checks}; /// Informs the compiler that the site which is calling this function is not /// reachable, possibly enabling further optimizations. @@ -111,41 +110,92 @@ pub const unsafe fn unreachable_unchecked() -> ! { /// Makes a *soundness* promise to the compiler that `cond` holds. /// -/// This may allow the optimizer to simplify things, -/// but it might also make the generated code slower. -/// Either way, calling it will most likely make compilation take longer. +/// This may allow the optimizer to simplify things, but it might also make the generated code +/// slower. Either way, calling it will most likely make compilation take longer. /// -/// This is a situational tool for micro-optimization, and is allowed to do nothing. -/// Any use should come with a repeatable benchmark to show the value -/// and allow removing it later should the optimizer get smarter and no longer need it. +/// You may know this from other places as +/// [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic) or, in C, +/// [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume). /// -/// The more complicated the condition the less likely this is to be fruitful. -/// For example, `assert_unchecked(foo.is_sorted())` is a complex enough value -/// that the compiler is unlikely to be able to take advantage of it. +/// This promotes a correctness requirement to a soundness requirement. Don't do that without +/// very good reason. /// -/// There's also no need to `assert_unchecked` basic properties of things. For -/// example, the compiler already knows the range of `count_ones`, so there's no -/// benefit to `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`. +/// # Usage /// -/// If ever you're tempted to write `assert_unchecked(false)`, then you're -/// actually looking for [`unreachable_unchecked()`]. +/// This is a situational tool for micro-optimization, and is allowed to do nothing. Any use +/// should come with a repeatable benchmark to show the value, with the expectation to drop it +/// later should the optimizer get smarter and no longer need it. /// -/// You may know this from other places -/// as [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic) -/// or [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume). +/// The more complicated the condition, the less likely this is to be useful. For example, +/// `assert_unchecked(foo.is_sorted())` is a complex enough value that the compiler is unlikely +/// to be able to take advantage of it. /// -/// This promotes a correctness requirement to a soundness requirement. -/// Don't do that without very good reason. +/// There's also no need to `assert_unchecked` basic properties of things. For example, the +/// compiler already knows the range of `count_ones`, so there is no benefit to +/// `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`. +/// +/// `assert_unchecked` is logically equivalent to `if !cond { unreachable_unchecked(); }`. If +/// ever you are tempted to write `assert_unchecked(false)`, you should instead use +/// [`unreachable_unchecked()`] directly. /// /// # Safety /// -/// `cond` must be `true`. It's immediate UB to call this with `false`. +/// `cond` must be `true`. It is immediate UB to call this with `false`. +/// +/// # Example +/// +/// ``` +/// use core::hint; /// +/// /// # Safety +/// /// +/// /// `p` must be nonnull and valid +/// pub unsafe fn next_value(p: *const i32) -> i32 { +/// // SAFETY: caller invariants guarantee that `p` is not null +/// unsafe { hint::assert_unchecked(!p.is_null()) } +/// +/// if p.is_null() { +/// return -1; +/// } else { +/// // SAFETY: caller invariants guarantee that `p` is valid +/// unsafe { *p + 1 } +/// } +/// } +/// ``` +/// +/// Without the `assert_unchecked`, the above function produces the following with optimizations +/// enabled: +/// +/// ```asm +/// next_value: +/// test rdi, rdi +/// je .LBB0_1 +/// mov eax, dword ptr [rdi] +/// inc eax +/// ret +/// .LBB0_1: +/// mov eax, -1 +/// ret +/// ``` +/// +/// Adding the assertion allows the optimizer to remove the extra check: +/// +/// ```asm +/// next_value: +/// mov eax, dword ptr [rdi] +/// inc eax +/// ret +/// ``` +/// +/// This example is quite unlike anything that would be used in the real world: it is redundant +/// to put an assertion right next to code that checks the same thing, and dereferencing a +/// pointer already has the builtin assumption that it is nonnull. However, it illustrates the +/// kind of changes the optimizer can make even when the behavior is less obviously related. +#[track_caller] #[inline(always)] #[doc(alias = "assume")] -#[track_caller] -#[unstable(feature = "hint_assert_unchecked", issue = "119131")] -#[rustc_const_unstable(feature = "const_hint_assert_unchecked", issue = "119131")] +#[stable(feature = "hint_assert_unchecked", since = "1.81.0")] +#[rustc_const_stable(feature = "hint_assert_unchecked", since = "1.81.0")] pub const unsafe fn assert_unchecked(cond: bool) { // SAFETY: The caller promised `cond` is true. unsafe { @@ -263,7 +313,7 @@ pub fn spin_loop() { /// extent to which it can block optimisations may vary depending upon the platform and code-gen /// backend used. Programs cannot rely on `black_box` for *correctness*, beyond it behaving as the /// identity function. As such, it **must not be relied upon to control critical program behavior.** -/// This _immediately_ precludes any direct use of this function for cryptographic or security +/// This also means that this function does not offer any guarantees for cryptographic or security /// purposes. /// /// [`std::convert::identity`]: crate::convert::identity diff --git a/library/core/src/internal_macros.rs b/library/core/src/internal_macros.rs index d3a4d6aff2d8b..fe4fa80263c28 100644 --- a/library/core/src/internal_macros.rs +++ b/library/core/src/internal_macros.rs @@ -80,6 +80,47 @@ macro_rules! forward_ref_op_assign { } } +/// Creates a zero-size type similar to a closure type, but named. +macro_rules! impl_fn_for_zst { + ($( + $( #[$attr: meta] )* + struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = + |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty + $body: block; + )+) => { + $( + $( #[$attr] )* + struct $Name; + + impl $( <$( $lifetime ),+> )? Fn<($( $ArgTy, )*)> for $Name { + #[inline] + extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { + $body + } + } + + impl $( <$( $lifetime ),+> )? FnMut<($( $ArgTy, )*)> for $Name { + #[inline] + extern "rust-call" fn call_mut( + &mut self, + ($( $arg, )*): ($( $ArgTy, )*) + ) -> $ReturnTy { + Fn::call(&*self, ($( $arg, )*)) + } + } + + impl $( <$( $lifetime ),+> )? FnOnce<($( $ArgTy, )*)> for $Name { + type Output = $ReturnTy; + + #[inline] + extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { + Fn::call(&self, ($( $arg, )*)) + } + } + )+ + } +} + /// A macro for defining `#[cfg]` if-else statements. /// /// `cfg_if` is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 1ab7ef9b5dc72..aa79c17763874 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -64,11 +64,8 @@ #![allow(missing_docs)] use safety::requires; -use crate::marker::DiscriminantKind; -use crate::marker::Tuple; -use crate::mem::align_of; -use crate::ptr; -use crate::ub_checks; +use crate::marker::{DiscriminantKind, Tuple}; +use crate::{ptr, ub_checks}; #[cfg(kani)] use crate::kani; @@ -952,7 +949,6 @@ extern "rust-intrinsic" { #[rustc_const_stable(feature = "const_unreachable_unchecked", since = "1.57.0")] #[rustc_nounwind] pub fn unreachable() -> !; - } /// Informs the optimizer that a condition is always true. @@ -964,7 +960,7 @@ extern "rust-intrinsic" { /// not be used if the invariant can be discovered by the optimizer on its /// own, or if it does not enable any significant optimizations. /// -/// This intrinsic does not have a stable counterpart. +/// The stabilized version of this intrinsic is [`core::hint::assert_unchecked`]. #[rustc_const_stable(feature = "const_assume", since = "1.77.0")] #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] @@ -991,7 +987,7 @@ pub const unsafe fn assume(b: bool) { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)] +#[miri::intrinsic_fallback_is_spec] pub const fn likely(b: bool) -> bool { b } @@ -1011,11 +1007,39 @@ pub const fn likely(b: bool) -> bool { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)] +#[miri::intrinsic_fallback_is_spec] pub const fn unlikely(b: bool) -> bool { b } +/// Returns either `true_val` or `false_val` depending on condition `b` with a +/// hint to the compiler that this condition is unlikely to be correctly +/// predicted by a CPU's branch predictor (e.g. a binary search). +/// +/// This is otherwise functionally equivalent to `if b { true_val } else { false_val }`. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +/// +/// This intrinsic does not have a stable counterpart. +#[cfg(not(bootstrap))] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +#[rustc_nounwind] +#[miri::intrinsic_fallback_is_spec] +#[inline] +pub fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { + if b { true_val } else { false_val } +} + +#[cfg(bootstrap)] +#[inline] +pub fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { + if b { true_val } else { false_val } +} + extern "rust-intrinsic" { /// Executes a breakpoint trap, for inspection by a debugger. /// @@ -1023,83 +1047,6 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn breakpoint(); - /// The size of a type in bytes. - /// - /// Note that, unlike most intrinsics, this is safe to call; - /// it does not require an `unsafe` block. - /// Therefore, implementations must not require the user to uphold - /// any safety invariants. - /// - /// More specifically, this is the offset in bytes between successive - /// items of the same type, including alignment padding. - /// - /// The stabilized version of this intrinsic is [`core::mem::size_of`]. - #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")] - #[rustc_safe_intrinsic] - #[rustc_nounwind] - pub fn size_of() -> usize; - - /// The minimum alignment of a type. - /// - /// Note that, unlike most intrinsics, this is safe to call; - /// it does not require an `unsafe` block. - /// Therefore, implementations must not require the user to uphold - /// any safety invariants. - /// - /// The stabilized version of this intrinsic is [`core::mem::align_of`]. - #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")] - #[rustc_safe_intrinsic] - #[rustc_nounwind] - pub fn min_align_of() -> usize; - /// The preferred alignment of a type. - /// - /// This intrinsic does not have a stable counterpart. - /// It's "tracking issue" is [#91971](https://github.com/rust-lang/rust/issues/91971). - #[rustc_const_unstable(feature = "const_pref_align_of", issue = "91971")] - #[rustc_nounwind] - pub fn pref_align_of() -> usize; - - /// The size of the referenced value in bytes. - /// - /// The stabilized version of this intrinsic is [`crate::mem::size_of_val`]. - #[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")] - #[rustc_nounwind] - pub fn size_of_val(_: *const T) -> usize; - /// The required alignment of the referenced value. - /// - /// The stabilized version of this intrinsic is [`core::mem::align_of_val`]. - #[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")] - #[rustc_nounwind] - pub fn min_align_of_val(_: *const T) -> usize; - - /// Gets a static string slice containing the name of a type. - /// - /// Note that, unlike most intrinsics, this is safe to call; - /// it does not require an `unsafe` block. - /// Therefore, implementations must not require the user to uphold - /// any safety invariants. - /// - /// The stabilized version of this intrinsic is [`core::any::type_name`]. - #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] - #[rustc_safe_intrinsic] - #[rustc_nounwind] - pub fn type_name() -> &'static str; - - /// Gets an identifier which is globally unique to the specified type. This - /// function will return the same value for a type regardless of whichever - /// crate it is invoked in. - /// - /// Note that, unlike most intrinsics, this is safe to call; - /// it does not require an `unsafe` block. - /// Therefore, implementations must not require the user to uphold - /// any safety invariants. - /// - /// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. - #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] - #[rustc_safe_intrinsic] - #[rustc_nounwind] - pub fn type_id() -> u128; - /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: /// This will statically either panic, or do nothing. /// @@ -1296,7 +1243,7 @@ extern "rust-intrinsic" { /// - If the code actually wants to work on the address the pointer points to, it can use `as` /// casts or [`ptr.addr()`][pointer::addr]. /// - /// Turning a `*mut T` into an `&mut T`: + /// Turning a `*mut T` into a `&mut T`: /// /// ``` /// let ptr: *mut i32 = &mut 0; @@ -1308,7 +1255,7 @@ extern "rust-intrinsic" { /// let ref_casted = unsafe { &mut *ptr }; /// ``` /// - /// Turning an `&mut T` into an `&mut U`: + /// Turning a `&mut T` into a `&mut U`: /// /// ``` /// let ptr = &mut 0; @@ -1321,7 +1268,7 @@ extern "rust-intrinsic" { /// let val_casts = unsafe { &mut *(ptr as *mut i32 as *mut u32) }; /// ``` /// - /// Turning an `&str` into a `&[u8]`: + /// Turning a `&str` into a `&[u8]`: /// /// ``` /// // this is not a good way to do this. @@ -1407,7 +1354,7 @@ extern "rust-intrinsic" { /// } /// /// // This gets rid of the type safety problems; `&mut *` will *only* give - /// // you an `&mut T` from an `&mut T` or `*mut T`. + /// // you a `&mut T` from a `&mut T` or `*mut T`. /// fn split_at_mut_casts(slice: &mut [T], mid: usize) /// -> (&mut [T], &mut [T]) { /// let len = slice.len(); @@ -1988,7 +1935,7 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] pub fn frem_algebraic(a: T, b: T) -> T; - /// Convert with LLVM’s fptoui/fptosi, which may return undef for values out of range + /// Converts with LLVM’s fptoui/fptosi, which may return undef for values out of range /// () /// /// Stabilized as [`f32::to_int_unchecked`] and [`f64::to_int_unchecked`]. @@ -2429,20 +2376,6 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn discriminant_value(v: &T) -> ::Discriminant; - /// Returns the number of variants of the type `T` cast to a `usize`; - /// if `T` has no variants, returns `0`. Uninhabited variants will be counted. - /// - /// Note that, unlike most intrinsics, this is safe to call; - /// it does not require an `unsafe` block. - /// Therefore, implementations must not require the user to uphold - /// any safety invariants. - /// - /// The to-be-stabilized version of this intrinsic is [`crate::mem::variant_count`]. - #[rustc_const_unstable(feature = "variant_count", issue = "73662")] - #[rustc_safe_intrinsic] - #[rustc_nounwind] - pub fn variant_count() -> usize; - /// Rust's "try catch" construct for unwinding. Invokes the function pointer `try_fn` with the /// data pointer `data`, and calls `catch_fn` if unwinding occurs while `try_fn` runs. /// @@ -2487,7 +2420,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] #[rustc_do_not_const_check] #[inline] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)] +#[miri::intrinsic_fallback_is_spec] pub const fn ptr_guaranteed_cmp(ptr: *const T, other: *const T) -> u8 { (ptr == other) as u8 } @@ -2507,11 +2440,13 @@ extern "rust-intrinsic" { /// /// # Safety /// - /// It's UB to call this if any of the *bytes* in `*a` or `*b` are uninitialized or carry a - /// pointer value. + /// It's UB to call this if any of the *bytes* in `*a` or `*b` are uninitialized. /// Note that this is a stricter criterion than just the *values* being /// fully-initialized: if `T` has padding, it's UB to call this intrinsic. /// + /// At compile-time, it is furthermore UB to call this if any of the bytes + /// in `*a` or `*b` have provenance. + /// /// (The implementation is allowed to branch on the results of comparisons, /// which is UB if any of their inputs are `undef`.) #[rustc_const_unstable(feature = "const_intrinsic_raw_eq", issue = "none")] @@ -2584,7 +2519,7 @@ extern "rust-intrinsic" { /// fn runtime() -> i32 { 1 } /// const fn compiletime() -> i32 { 2 } /// -// // ⚠ This code violates the required equivalence of `compiletime` +/// // ⚠ This code violates the required equivalence of `compiletime` /// // and `runtime`. /// const_eval_select((), compiletime, runtime) /// } @@ -2758,7 +2693,7 @@ pub const fn ub_checks() -> bool { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_nounwind] #[rustc_intrinsic] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)] +#[miri::intrinsic_fallback_is_spec] pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 { // const eval overrides this function, but runtime code for now just returns null pointers. // See . @@ -2779,13 +2714,16 @@ pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_nounwind] #[rustc_intrinsic] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)] +#[miri::intrinsic_fallback_is_spec] pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) { // Runtime NOP } -/// `ptr` must point to a vtable. /// The intrinsic will return the size stored in that vtable. +/// +/// # Safety +/// +/// `ptr` must point to a vtable. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] @@ -2794,8 +2732,11 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize { unreachable!() } -/// `ptr` must point to a vtable. /// The intrinsic will return the alignment stored in that vtable. +/// +/// # Safety +/// +/// `ptr` must point to a vtable. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] @@ -2804,6 +2745,142 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize { unreachable!() } +/// The size of a type in bytes. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +/// +/// More specifically, this is the offset in bytes between successive +/// items of the same type, including alignment padding. +/// +/// The stabilized version of this intrinsic is [`core::mem::size_of`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_stable(feature = "const_size_of", since = "1.40.0")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub const fn size_of() -> usize { + unreachable!() +} + +/// The minimum alignment of a type. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +/// +/// The stabilized version of this intrinsic is [`core::mem::align_of`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub const fn min_align_of() -> usize { + unreachable!() +} + +/// The preferred alignment of a type. +/// +/// This intrinsic does not have a stable counterpart. +/// It's "tracking issue" is [#91971](https://github.com/rust-lang/rust/issues/91971). +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_unstable(feature = "const_pref_align_of", issue = "91971")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub const unsafe fn pref_align_of() -> usize { + unreachable!() +} + +/// Returns the number of variants of the type `T` cast to a `usize`; +/// if `T` has no variants, returns `0`. Uninhabited variants will be counted. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +/// +/// The to-be-stabilized version of this intrinsic is [`crate::mem::variant_count`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_unstable(feature = "variant_count", issue = "73662")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub const fn variant_count() -> usize { + unreachable!() +} + +/// The size of the referenced value in bytes. +/// +/// The stabilized version of this intrinsic is [`crate::mem::size_of_val`]. +/// +/// # Safety +/// +/// See [`crate::mem::size_of_val_raw`] for safety conditions. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub const unsafe fn size_of_val(_ptr: *const T) -> usize { + unreachable!() +} + +/// The required alignment of the referenced value. +/// +/// The stabilized version of this intrinsic is [`core::mem::align_of_val`]. +/// +/// # Safety +/// +/// See [`crate::mem::align_of_val_raw`] for safety conditions. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub const unsafe fn min_align_of_val(_ptr: *const T) -> usize { + unreachable!() +} + +/// Gets a static string slice containing the name of a type. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +/// +/// The stabilized version of this intrinsic is [`core::any::type_name`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_unstable(feature = "const_type_name", issue = "63084")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub const fn type_name() -> &'static str { + unreachable!() +} + +/// Gets an identifier which is globally unique to the specified type. This +/// function will return the same value for a type regardless of whichever +/// crate it is invoked in. +/// +/// Note that, unlike most intrinsics, this is safe to call; +/// it does not require an `unsafe` block. +/// Therefore, implementations must not require the user to uphold +/// any safety invariants. +/// +/// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_unstable(feature = "const_type_id", issue = "77125")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub const fn type_id() -> u128 { + unreachable!() +} + /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and @@ -2831,6 +2908,20 @@ impl AggregateRawPtr<*mut T> for *mut P { type Metadata =

::Metadata; } +/// Lowers in MIR to `Rvalue::UnaryOp` with `UnOp::PtrMetadata`. +/// +/// This is used to implement functions like `ptr::metadata`. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +pub const fn ptr_metadata + ?Sized, M>(_ptr: *const P) -> M { + // To implement a fallback we'd have to assume the layout of the pointer, + // but the whole point of this intrinsic is that we shouldn't do that. + unreachable!() +} + // Some functions are defined here because they accidentally got made // available in this module on stable. See . // (`transmute` also falls into this category, but it cannot be wrapped due to the @@ -3040,8 +3131,7 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { unsafe { ub_checks::assert_unsafe_precondition!( check_language_ub, - "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ - and the specified memory ranges do not overlap", + "ptr::copy requires that both pointer arguments are aligned and non-null", ( src: *const () = src as *const (), dst: *mut () = dst as *mut (), diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index 02665b2676cc1..c7cec396e1f2e 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -20,7 +20,7 @@ //! //! #[custom_mir(dialect = "built")] //! pub fn simple(x: i32) -> i32 { -//! mir!( +//! mir! { //! let temp2: i32; //! //! { @@ -33,7 +33,7 @@ //! RET = temp2; //! Return() //! } -//! ) +//! } //! } //! ``` //! @@ -71,7 +71,7 @@ //! //! #[custom_mir(dialect = "built")] //! pub fn choose_load(a: &i32, b: &i32, c: bool) -> i32 { -//! mir!( +//! mir! { //! { //! match c { //! true => t, @@ -93,20 +93,22 @@ //! RET = *temp; //! Return() //! } -//! ) +//! } //! } //! //! #[custom_mir(dialect = "built")] //! fn unwrap_unchecked(opt: Option) -> T { -//! mir!({ -//! RET = Move(Field(Variant(opt, 1), 0)); -//! Return() -//! }) +//! mir! { +//! { +//! RET = Move(Field(Variant(opt, 1), 0)); +//! Return() +//! } +//! } //! } //! //! #[custom_mir(dialect = "runtime", phase = "optimized")] //! fn push_and_pop(v: &mut Vec, value: T) { -//! mir!( +//! mir! { //! let _unused; //! let popped; //! @@ -125,19 +127,19 @@ //! ret = { //! Return() //! } -//! ) +//! } //! } //! //! #[custom_mir(dialect = "runtime", phase = "optimized")] //! fn annotated_return_type() -> (i32, bool) { -//! mir!( +//! mir! { //! type RET = (i32, bool); //! { //! RET.0 = 1; //! RET.1 = true; //! Return() //! } -//! ) +//! } //! } //! ``` //! @@ -152,7 +154,7 @@ //! //! #[custom_mir(dialect = "built")] //! fn borrow_error(should_init: bool) -> i32 { -//! mir!( +//! mir! { //! let temp: i32; //! //! { @@ -171,7 +173,7 @@ //! RET = temp; //! Return() //! } -//! ) +//! } //! } //! ``` //! @@ -179,7 +181,7 @@ //! error[E0381]: used binding is possibly-uninitialized //! --> test.rs:24:13 //! | -//! 8 | / mir!( +//! 8 | / mir! { //! 9 | | let temp: i32; //! 10 | | //! 11 | | { @@ -191,7 +193,7 @@ //! | | ^^^^^^^^^^ value used here but it is possibly-uninitialized //! 25 | | Return() //! 26 | | } -//! 27 | | ) +//! 27 | | } //! | |_____- binding declared here but left uninitialized //! //! error: aborting due to 1 previous error @@ -245,6 +247,8 @@ //! otherwise branch. //! - [`Call`] has an associated function as well, with special syntax: //! `Call(ret_val = function(arg1, arg2, ...), ReturnTo(next_block), UnwindContinue())`. +//! - [`TailCall`] does not have a return destination or next block, so its syntax is just +//! `TailCall(function(arg1, arg2, ...))`. #![unstable( feature = "custom_mir", @@ -274,8 +278,7 @@ pub enum UnwindTerminateReason { InCleanup, } -pub use UnwindTerminateReason::Abi as ReasonAbi; -pub use UnwindTerminateReason::InCleanup as ReasonInCleanup; +pub use UnwindTerminateReason::{Abi as ReasonAbi, InCleanup as ReasonInCleanup}; macro_rules! define { ($name:literal, $( #[ $meta:meta ] )* fn $($sig:tt)*) => { @@ -349,6 +352,12 @@ define!("mir_call", /// - [`UnwindCleanup`] fn Call(call: (), goto: ReturnToArg, unwind_action: UnwindActionArg) ); +define!("mir_tail_call", + /// Call a function. + /// + /// The argument must be of the form `fun(arg1, arg2, ...)`. + fn TailCall(call: T) +); define!("mir_unwind_resume", /// A terminator that resumes the unwinding. fn UnwindResume() @@ -360,6 +369,10 @@ define!("mir_assume", fn Assume(operand: bool)); define!("mir_deinit", fn Deinit(place: T)); define!("mir_checked", fn Checked(binop: T) -> (T, bool)); define!("mir_len", fn Len(place: T) -> usize); +define!( + "mir_ptr_metadata", + fn PtrMetadata(place: *const P) ->

::Metadata +); define!("mir_copy_for_deref", fn CopyForDeref(place: T) -> T); define!("mir_retag", fn Retag(place: T)); define!("mir_move", fn Move(place: T) -> T); @@ -403,18 +416,22 @@ define!( /// /// #[custom_mir(dialect = "built")] /// fn unwrap_deref(opt: Option<&i32>) -> i32 { - /// mir!({ - /// RET = *Field::<&i32>(Variant(opt, 1), 0); - /// Return() - /// }) + /// mir! { + /// { + /// RET = *Field::<&i32>(Variant(opt, 1), 0); + /// Return() + /// } + /// } /// } /// /// #[custom_mir(dialect = "built")] /// fn set(opt: &mut Option) { - /// mir!({ - /// place!(Field(Variant(*opt, 1), 0)) = 5; - /// Return() - /// }) + /// mir! { + /// { + /// place!(Field(Variant(*opt, 1), 0)) = 5; + /// Return() + /// } + /// } /// } /// ``` fn Field(place: (), field: u32) -> F @@ -434,6 +451,13 @@ define!( /// generated via the normal `mem::transmute`. fn CastTransmute(operand: T) -> U ); +define!( + "mir_cast_ptr_to_ptr", + /// Emits a `CastKind::PtrToPtr` cast. + /// + /// This allows bypassing normal validation to generate strange casts. + fn CastPtrToPtr(operand: T) -> U +); define!( "mir_make_place", #[doc(hidden)] @@ -451,7 +475,7 @@ define!( /// your MIR into something that is easier to parse in the compiler. #[rustc_macro_transparency = "transparent"] pub macro mir { - ( + { $(type RET = $ret_ty:ty ;)? $(let $local_decl:ident $(: $local_decl_ty:ty)? ;)* $(debug $dbg_name:ident => $dbg_data:expr ;)* @@ -465,7 +489,7 @@ pub macro mir { $($block:tt)* } )* - ) => {{ + } => {{ // First, we declare all basic blocks. __internal_declare_basic_blocks!($( $block_name $(($block_cleanup))? diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index d1be534eaf083..221724d7b4ae9 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -3,7 +3,7 @@ //! In this module, a "vector" is any `repr(simd)` type. extern "rust-intrinsic" { - /// Insert an element into a vector, returning the updated vector. + /// Inserts an element into a vector, returning the updated vector. /// /// `T` must be a vector with element type `U`. /// @@ -13,7 +13,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_insert(x: T, idx: u32, val: U) -> T; - /// Extract an element from a vector. + /// Extracts an element from a vector. /// /// `T` must be a vector with element type `U`. /// @@ -23,25 +23,25 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_extract(x: T, idx: u32) -> U; - /// Add two simd vectors elementwise. + /// Adds two simd vectors elementwise. /// /// `T` must be a vector of integer or floating point primitive types. #[rustc_nounwind] pub fn simd_add(x: T, y: T) -> T; - /// Subtract `rhs` from `lhs` elementwise. + /// Subtracts `rhs` from `lhs` elementwise. /// /// `T` must be a vector of integer or floating point primitive types. #[rustc_nounwind] pub fn simd_sub(lhs: T, rhs: T) -> T; - /// Multiply two simd vectors elementwise. + /// Multiplies two simd vectors elementwise. /// /// `T` must be a vector of integer or floating point primitive types. #[rustc_nounwind] pub fn simd_mul(x: T, y: T) -> T; - /// Divide `lhs` by `rhs` elementwise. + /// Divides `lhs` by `rhs` elementwise. /// /// `T` must be a vector of integer or floating point primitive types. /// @@ -51,7 +51,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_div(lhs: T, rhs: T) -> T; - /// Remainder of two vectors elementwise + /// Returns remainder of two vectors elementwise. /// /// `T` must be a vector of integer or floating point primitive types. /// @@ -61,9 +61,9 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_rem(lhs: T, rhs: T) -> T; - /// Elementwise vector left shift, with UB on overflow. + /// Shifts vector left elementwise, with UB on overflow. /// - /// Shift `lhs` left by `rhs`, shifting in sign bits for signed types. + /// Shifts `lhs` left by `rhs`, shifting in sign bits for signed types. /// /// `T` must be a vector of integer primitive types. /// @@ -73,11 +73,11 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_shl(lhs: T, rhs: T) -> T; - /// Elementwise vector right shift, with UB on overflow. + /// Shifts vector right elementwise, with UB on overflow. /// /// `T` must be a vector of integer primitive types. /// - /// Shift `lhs` right by `rhs`, shifting in sign bits for signed types. + /// Shifts `lhs` right by `rhs`, shifting in sign bits for signed types. /// /// # Safety /// @@ -85,25 +85,25 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_shr(lhs: T, rhs: T) -> T; - /// Elementwise vector "and". + /// "Ands" vectors elementwise. /// /// `T` must be a vector of integer primitive types. #[rustc_nounwind] pub fn simd_and(x: T, y: T) -> T; - /// Elementwise vector "or". + /// "Ors" vectors elementwise. /// /// `T` must be a vector of integer primitive types. #[rustc_nounwind] pub fn simd_or(x: T, y: T) -> T; - /// Elementwise vector "exclusive or". + /// "Exclusive ors" vectors elementwise. /// /// `T` must be a vector of integer primitive types. #[rustc_nounwind] pub fn simd_xor(x: T, y: T) -> T; - /// Numerically cast a vector, elementwise. + /// Numerically casts a vector, elementwise. /// /// `T` and `U` must be vectors of integer or floating point primitive types, and must have the /// same length. @@ -124,7 +124,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_cast(x: T) -> U; - /// Numerically cast a vector, elementwise. + /// Numerically casts a vector, elementwise. /// /// `T` and `U` be a vectors of integer or floating point primitive types, and must have the /// same length. @@ -138,7 +138,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_as(x: T) -> U; - /// Elementwise negation of a vector. + /// Negates a vector elementwise. /// /// `T` must be a vector of integer or floating-point primitive types. /// @@ -146,13 +146,13 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_neg(x: T) -> T; - /// Elementwise absolute value of a vector. + /// Returns absolute value of a vector, elementwise. /// /// `T` must be a vector of floating-point primitive types. #[rustc_nounwind] pub fn simd_fabs(x: T) -> T; - /// Elementwise minimum of a vector. + /// Returns the minimum of two vectors, elementwise. /// /// `T` must be a vector of floating-point primitive types. /// @@ -160,7 +160,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_fmin(x: T, y: T) -> T; - /// Elementwise maximum of a vector. + /// Returns the maximum of two vectors, elementwise. /// /// `T` must be a vector of floating-point primitive types. /// @@ -228,7 +228,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_ge(x: T, y: T) -> U; - /// Shuffle two vectors by const indices. + /// Shuffles two vectors by const indices. /// /// `T` must be a vector. /// @@ -243,19 +243,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_shuffle(x: T, y: T, idx: U) -> V; - /// Shuffle two vectors by const indices. - /// - /// `T` must be a vector. - /// - /// `U` must be a vector with the same element type as `T` and the same length as `IDX`. - /// - /// Returns a new vector such that element `i` is selected from `xy[IDX[i]]`, where `xy` - /// is the concatenation of `x` and `y`. It is a compile-time error if `IDX[i]` is out-of-bounds - /// of `xy`. - #[rustc_nounwind] - pub fn simd_shuffle_generic(x: T, y: T) -> U; - - /// Read a vector of pointers. + /// Reads a vector of pointers. /// /// `T` must be a vector. /// @@ -263,9 +251,6 @@ extern "rust-intrinsic" { /// /// `V` must be a vector of integers with the same length as `T` (but any element size). /// - /// `idx` must be a constant: either naming a constant item, or an inline - /// `const {}` expression. - /// /// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, read the pointer. /// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from /// `val`. @@ -278,7 +263,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_gather(val: T, ptr: U, mask: V) -> T; - /// Write to a vector of pointers. + /// Writes to a vector of pointers. /// /// `T` must be a vector. /// @@ -301,7 +286,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_scatter(val: T, ptr: U, mask: V); - /// Read a vector of pointers. + /// Reads a vector of pointers. /// /// `T` must be a vector. /// @@ -323,7 +308,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_masked_load(mask: V, ptr: U, val: T) -> T; - /// Write to a vector of pointers. + /// Writes to a vector of pointers. /// /// `T` must be a vector. /// @@ -344,13 +329,13 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_masked_store(mask: V, ptr: U, val: T); - /// Add two simd vectors elementwise, with saturation. + /// Adds two simd vectors elementwise, with saturation. /// /// `T` must be a vector of integer primitive types. #[rustc_nounwind] pub fn simd_saturating_add(x: T, y: T) -> T; - /// Subtract two simd vectors elementwise, with saturation. + /// Subtracts two simd vectors elementwise, with saturation. /// /// `T` must be a vector of integer primitive types. /// @@ -358,7 +343,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_saturating_sub(lhs: T, rhs: T) -> T; - /// Add elements within a vector from left to right. + /// Adds elements within a vector from left to right. /// /// `T` must be a vector of integer or floating-point primitive types. /// @@ -368,7 +353,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_add_ordered(x: T, y: U) -> U; - /// Add elements within a vector in arbitrary order. May also be re-associated with + /// Adds elements within a vector in arbitrary order. May also be re-associated with /// unordered additions on the inputs/outputs. /// /// `T` must be a vector of integer or floating-point primitive types. @@ -377,7 +362,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_add_unordered(x: T) -> U; - /// Multiply elements within a vector from left to right. + /// Multiplies elements within a vector from left to right. /// /// `T` must be a vector of integer or floating-point primitive types. /// @@ -387,7 +372,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_mul_ordered(x: T, y: U) -> U; - /// Add elements within a vector in arbitrary order. May also be re-associated with + /// Multiplies elements within a vector in arbitrary order. May also be re-associated with /// unordered additions on the inputs/outputs. /// /// `T` must be a vector of integer or floating-point primitive types. @@ -396,7 +381,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_mul_unordered(x: T) -> U; - /// Check if all mask values are true. + /// Checks if all mask values are true. /// /// `T` must be a vector of integer primitive types. /// @@ -405,7 +390,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_all(x: T) -> bool; - /// Check if all mask values are true. + /// Checks if any mask value is true. /// /// `T` must be a vector of integer primitive types. /// @@ -414,7 +399,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_any(x: T) -> bool; - /// Return the maximum element of a vector. + /// Returns the maximum element of a vector. /// /// `T` must be a vector of integer or floating-point primitive types. /// @@ -424,7 +409,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_max(x: T) -> U; - /// Return the minimum element of a vector. + /// Returns the minimum element of a vector. /// /// `T` must be a vector of integer or floating-point primitive types. /// @@ -434,7 +419,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_min(x: T) -> U; - /// Logical "and" all elements together. + /// Logical "ands" all elements together. /// /// `T` must be a vector of integer or floating-point primitive types. /// @@ -442,7 +427,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_and(x: T) -> U; - /// Logical "or" all elements together. + /// Logical "ors" all elements together. /// /// `T` must be a vector of integer or floating-point primitive types. /// @@ -450,7 +435,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_or(x: T) -> U; - /// Logical "exclusive or" all elements together. + /// Logical "exclusive ors" all elements together. /// /// `T` must be a vector of integer or floating-point primitive types. /// @@ -458,12 +443,12 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_xor(x: T) -> U; - /// Truncate an integer vector to a bitmask. + /// Truncates an integer vector to a bitmask. /// /// `T` must be an integer vector. /// /// `U` must be either the smallest unsigned integer with at least as many bits as the length - /// of `T`, or the smallest array of `u8` with as many bits as the length of `T`. + /// of `T`, or the smallest array of `u8` with at least as many bits as the length of `T`. /// /// Each element is truncated to a single bit and packed into the result. /// @@ -475,19 +460,26 @@ extern "rust-intrinsic" { /// * On little endian, the least significant bit corresponds to the first vector element. /// * On big endian, the least significant bit corresponds to the last vector element. /// - /// For example, `[!0, 0, !0, !0]` packs to `0b1101` on little endian and `0b1011` on big - /// endian. + /// For example, `[!0, 0, !0, !0]` packs to + /// - `0b1101u8` or `[0b1101]` on little endian, and + /// - `0b1011u8` or `[0b1011]` on big endian. + /// + /// To consider a larger example, + /// `[!0, 0, 0, 0, 0, 0, 0, 0, !0, !0, 0, 0, 0, 0, !0, 0]` packs to + /// - `0b0100001100000001u16` or `[0b00000001, 0b01000011]` on little endian, and + /// - `0b1000000011000010u16` or `[0b10000000, 0b11000010]` on big endian. /// - /// To consider a larger example, `[!0, 0, 0, 0, 0, 0, 0, 0, !0, !0, 0, 0, 0, 0, !0, 0]` packs - /// to `[0b00000001, 0b01000011]` or `0b0100001100000001` on little endian, and `[0b10000000, - /// 0b11000010]` or `0b1000000011000010` on big endian. + /// And finally, a non-power-of-2 example with multiple bytes: + /// `[!0, !0, 0, !0, 0, 0, !0, 0, !0, 0]` packs to + /// - `0b0101001011u16` or `[0b01001011, 0b01]` on little endian, and + /// - `0b1101001010u16` or `[0b11, 0b01001010]` on big endian. /// /// # Safety /// `x` must contain only `0` and `!0`. #[rustc_nounwind] pub fn simd_bitmask(x: T) -> U; - /// Select elements from a mask. + /// Selects elements from a mask. /// /// `M` must be an integer vector. /// @@ -502,7 +494,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_select(mask: M, if_true: T, if_false: T) -> T; - /// Select elements from a bitmask. + /// Selects elements from a bitmask. /// /// `M` must be an unsigned integer or array of `u8`, matching `simd_bitmask`. /// @@ -519,7 +511,8 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_select_bitmask(m: M, yes: T, no: T) -> T; - /// Elementwise calculates the offset from a pointer vector, potentially wrapping. + /// Calculates the offset from a pointer vector elementwise, potentially + /// wrapping. /// /// `T` must be a vector of pointers. /// @@ -529,13 +522,13 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_arith_offset(ptr: T, offset: U) -> T; - /// Cast a vector of pointers. + /// Casts a vector of pointers. /// /// `T` and `U` must be vectors of pointers with the same number of elements. #[rustc_nounwind] pub fn simd_cast_ptr(ptr: T) -> U; - /// Expose a vector of pointers as a vector of addresses. + /// Exposes a vector of pointers as a vector of addresses. /// /// `T` must be a vector of pointers. /// @@ -543,7 +536,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_expose_provenance(ptr: T) -> U; - /// Create a vector of pointers from a vector of addresses. + /// Creates a vector of pointers from a vector of addresses. /// /// `T` must be a vector of `usize`. /// @@ -551,57 +544,56 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_with_exposed_provenance(addr: T) -> U; - /// Swap bytes of each element. + /// Swaps bytes of each element. /// /// `T` must be a vector of integers. #[rustc_nounwind] pub fn simd_bswap(x: T) -> T; - /// Reverse bits of each element. + /// Reverses bits of each element. /// /// `T` must be a vector of integers. #[rustc_nounwind] pub fn simd_bitreverse(x: T) -> T; - /// Count the leading zeros of each element. + /// Counts the leading zeros of each element. /// /// `T` must be a vector of integers. #[rustc_nounwind] pub fn simd_ctlz(x: T) -> T; - /// Count the number of ones in each element. + /// Counts the number of ones in each element. /// /// `T` must be a vector of integers. #[rustc_nounwind] - #[cfg(not(bootstrap))] pub fn simd_ctpop(x: T) -> T; - /// Count the trailing zeros of each element. + /// Counts the trailing zeros of each element. /// /// `T` must be a vector of integers. #[rustc_nounwind] pub fn simd_cttz(x: T) -> T; - /// Round up each element to the next highest integer-valued float. + /// Rounds up each element to the next highest integer-valued float. /// /// `T` must be a vector of floats. #[rustc_nounwind] pub fn simd_ceil(x: T) -> T; - /// Round down each element to the next lowest integer-valued float. + /// Rounds down each element to the next lowest integer-valued float. /// /// `T` must be a vector of floats. #[rustc_nounwind] pub fn simd_floor(x: T) -> T; - /// Round each element to the closest integer-valued float. + /// Rounds each element to the closest integer-valued float. /// Ties are resolved by rounding away from 0. /// /// `T` must be a vector of floats. #[rustc_nounwind] pub fn simd_round(x: T) -> T; - /// Return the integer part of each element as an integer-valued float. + /// Returns the integer part of each element as an integer-valued float. /// In other words, non-integer values are truncated towards zero. /// /// `T` must be a vector of floats. diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index d497da33dd923..dbc60aa8154c6 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -44,7 +44,7 @@ impl Debug for BorrowedBuf<'_> { } } -/// Create a new `BorrowedBuf` from a fully initialized slice. +/// Creates a new `BorrowedBuf` from a fully initialized slice. impl<'data> From<&'data mut [u8]> for BorrowedBuf<'data> { #[inline] fn from(slice: &'data mut [u8]) -> BorrowedBuf<'data> { @@ -59,7 +59,7 @@ impl<'data> From<&'data mut [u8]> for BorrowedBuf<'data> { } } -/// Create a new `BorrowedBuf` from an uninitialized buffer. +/// Creates a new `BorrowedBuf` from an uninitialized buffer. /// /// Use `set_init` if part of the buffer is known to be already initialized. impl<'data> From<&'data mut [MaybeUninit]> for BorrowedBuf<'data> { @@ -174,7 +174,7 @@ pub struct BorrowedCursor<'a> { } impl<'a> BorrowedCursor<'a> { - /// Reborrow this cursor by cloning it with a smaller lifetime. + /// Reborrows this cursor by cloning it with a smaller lifetime. /// /// Since a cursor maintains unique access to its underlying buffer, the borrowed cursor is /// not accessible while the new cursor exists. @@ -247,7 +247,7 @@ impl<'a> BorrowedCursor<'a> { unsafe { self.buf.buf.get_unchecked_mut(self.buf.filled..) } } - /// Advance the cursor by asserting that `n` bytes have been filled. + /// Advances the cursor by asserting that `n` bytes have been filled. /// /// After advancing, the `n` bytes are no longer accessible via the cursor and can only be /// accessed via the underlying buffer. I.e., the buffer's filled portion grows by `n` elements @@ -268,7 +268,7 @@ impl<'a> BorrowedCursor<'a> { self } - /// Advance the cursor by asserting that `n` bytes have been filled. + /// Advances the cursor by asserting that `n` bytes have been filled. /// /// After advancing, the `n` bytes are no longer accessible via the cursor and can only be /// accessed via the underlying buffer. I.e., the buffer's filled portion grows by `n` elements diff --git a/library/core/src/iter/adapters/chain.rs b/library/core/src/iter/adapters/chain.rs index bcaac2f42cf04..dad3d79acb183 100644 --- a/library/core/src/iter/adapters/chain.rs +++ b/library/core/src/iter/adapters/chain.rs @@ -4,8 +4,8 @@ use crate::ops::Try; /// An iterator that links two iterators together, in a chain. /// -/// This `struct` is created by [`Iterator::chain`]. See its documentation -/// for more. +/// This `struct` is created by [`chain`] or [`Iterator::chain`]. See their +/// documentation for more. /// /// # Examples /// @@ -38,6 +38,39 @@ impl Chain { } } +/// Converts the arguments to iterators and links them together, in a chain. +/// +/// See the documentation of [`Iterator::chain`] for more. +/// +/// # Examples +/// +/// ``` +/// #![feature(iter_chain)] +/// +/// use std::iter::chain; +/// +/// let a = [1, 2, 3]; +/// let b = [4, 5, 6]; +/// +/// let mut iter = chain(a, b); +/// +/// assert_eq!(iter.next(), Some(1)); +/// assert_eq!(iter.next(), Some(2)); +/// assert_eq!(iter.next(), Some(3)); +/// assert_eq!(iter.next(), Some(4)); +/// assert_eq!(iter.next(), Some(5)); +/// assert_eq!(iter.next(), Some(6)); +/// assert_eq!(iter.next(), None); +/// ``` +#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +pub fn chain(a: A, b: B) -> Chain +where + A: IntoIterator, + B: IntoIterator, +{ + Chain::new(a.into_iter(), b.into_iter()) +} + #[stable(feature = "rust1", since = "1.0.0")] impl Iterator for Chain where diff --git a/library/core/src/iter/adapters/cloned.rs b/library/core/src/iter/adapters/cloned.rs index 1a106ef97942b..aea6d64281aec 100644 --- a/library/core/src/iter/adapters/cloned.rs +++ b/library/core/src/iter/adapters/cloned.rs @@ -1,9 +1,9 @@ -use crate::iter::adapters::{ - zip::try_get_unchecked, SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce, -}; +use core::num::NonZero; + +use crate::iter::adapters::zip::try_get_unchecked; +use crate::iter::adapters::{SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce}; use crate::iter::{FusedIterator, InPlaceIterable, TrustedLen, UncheckedIterator}; use crate::ops::Try; -use core::num::NonZero; /// An iterator that clones the elements of an underlying iterator. /// diff --git a/library/core/src/iter/adapters/copied.rs b/library/core/src/iter/adapters/copied.rs index 6d82d1581f79d..23e4e25ab5388 100644 --- a/library/core/src/iter/adapters/copied.rs +++ b/library/core/src/iter/adapters/copied.rs @@ -1,9 +1,7 @@ -use crate::iter::adapters::{ - zip::try_get_unchecked, SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce, -}; +use crate::iter::adapters::zip::try_get_unchecked; +use crate::iter::adapters::{SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce}; use crate::iter::{FusedIterator, InPlaceIterable, TrustedLen}; -use crate::mem::MaybeUninit; -use crate::mem::SizedTypeProperties; +use crate::mem::{MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::Try; use crate::{array, ptr}; @@ -202,7 +200,7 @@ where T: Copy, { fn spec_next_chunk(&mut self) -> Result<[T; N], array::IntoIter> { - let mut raw_array = MaybeUninit::uninit_array(); + let mut raw_array = [const { MaybeUninit::uninit() }; N]; let len = self.len(); diff --git a/library/core/src/iter/adapters/cycle.rs b/library/core/src/iter/adapters/cycle.rs index b35ed8442032d..6cb1a3a46763e 100644 --- a/library/core/src/iter/adapters/cycle.rs +++ b/library/core/src/iter/adapters/cycle.rs @@ -1,5 +1,6 @@ +use crate::iter::FusedIterator; use crate::num::NonZero; -use crate::{iter::FusedIterator, ops::Try}; +use crate::ops::Try; /// An iterator that repeats endlessly. /// diff --git a/library/core/src/iter/adapters/enumerate.rs b/library/core/src/iter/adapters/enumerate.rs index 7adbabf69e490..ac15e3767fc09 100644 --- a/library/core/src/iter/adapters/enumerate.rs +++ b/library/core/src/iter/adapters/enumerate.rs @@ -1,6 +1,5 @@ -use crate::iter::adapters::{ - zip::try_get_unchecked, SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce, -}; +use crate::iter::adapters::zip::try_get_unchecked; +use crate::iter::adapters::{SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce}; use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused, TrustedLen}; use crate::num::NonZero; use crate::ops::Try; diff --git a/library/core/src/iter/adapters/filter.rs b/library/core/src/iter/adapters/filter.rs index a7f1fde6975c0..dd08cd6f61c4c 100644 --- a/library/core/src/iter/adapters/filter.rs +++ b/library/core/src/iter/adapters/filter.rs @@ -1,10 +1,12 @@ +use core::array; +use core::mem::MaybeUninit; +use core::ops::ControlFlow; + use crate::fmt; -use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedFused}; +use crate::iter::adapters::SourceIter; +use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused}; use crate::num::NonZero; use crate::ops::Try; -use core::array; -use core::mem::{ManuallyDrop, MaybeUninit}; -use core::ops::ControlFlow; /// An iterator that filters the elements of `iter` with `predicate`. /// @@ -27,6 +29,42 @@ impl Filter { } } +impl Filter +where + I: Iterator, + P: FnMut(&I::Item) -> bool, +{ + #[inline] + fn next_chunk_dropless( + &mut self, + ) -> Result<[I::Item; N], array::IntoIter> { + let mut array: [MaybeUninit; N] = [const { MaybeUninit::uninit() }; N]; + let mut initialized = 0; + + let result = self.iter.try_for_each(|element| { + let idx = initialized; + // branchless index update combined with unconditionally copying the value even when + // it is filtered reduces branching and dependencies in the loop. + initialized = idx + (self.predicate)(&element) as usize; + // SAFETY: Loop conditions ensure the index is in bounds. + unsafe { array.get_unchecked_mut(idx) }.write(element); + + if initialized < N { ControlFlow::Continue(()) } else { ControlFlow::Break(()) } + }); + + match result { + ControlFlow::Break(()) => { + // SAFETY: The loop above is only explicitly broken when the array has been fully initialized + Ok(unsafe { MaybeUninit::array_assume_init(array) }) + } + ControlFlow::Continue(()) => { + // SAFETY: The range is in bounds since the loop breaks when reaching N elements. + Err(unsafe { array::IntoIter::new_unchecked(array, 0..initialized) }) + } + } + } +} + #[stable(feature = "core_impl_debug", since = "1.9.0")] impl fmt::Debug for Filter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -64,52 +102,16 @@ where fn next_chunk( &mut self, ) -> Result<[Self::Item; N], array::IntoIter> { - let mut array: [MaybeUninit; N] = MaybeUninit::uninit_array(); - - struct Guard<'a, T> { - array: &'a mut [MaybeUninit], - initialized: usize, - } - - impl Drop for Guard<'_, T> { - #[inline] - fn drop(&mut self) { - if const { crate::mem::needs_drop::() } { - // SAFETY: self.initialized is always <= N, which also is the length of the array. - unsafe { - core::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( - self.array.get_unchecked_mut(..self.initialized), - )); - } - } + // avoid codegen for the dead branch + let fun = const { + if crate::mem::needs_drop::() { + array::iter_next_chunk:: + } else { + Self::next_chunk_dropless:: } - } + }; - let mut guard = Guard { array: &mut array, initialized: 0 }; - - let result = self.iter.try_for_each(|element| { - let idx = guard.initialized; - guard.initialized = idx + (self.predicate)(&element) as usize; - - // SAFETY: Loop conditions ensure the index is in bounds. - unsafe { guard.array.get_unchecked_mut(idx) }.write(element); - - if guard.initialized < N { ControlFlow::Continue(()) } else { ControlFlow::Break(()) } - }); - - let guard = ManuallyDrop::new(guard); - - match result { - ControlFlow::Break(()) => { - // SAFETY: The loop above is only explicitly broken when the array has been fully initialized - Ok(unsafe { MaybeUninit::array_assume_init(array) }) - } - ControlFlow::Continue(()) => { - let initialized = guard.initialized; - // SAFETY: The range is in bounds since the loop breaks when reaching N elements. - Err(unsafe { array::IntoIter::new_unchecked(array, 0..initialized) }) - } - } + fun(self) } #[inline] diff --git a/library/core/src/iter/adapters/filter_map.rs b/library/core/src/iter/adapters/filter_map.rs index 1a5f9e6265454..914ef6131771f 100644 --- a/library/core/src/iter/adapters/filter_map.rs +++ b/library/core/src/iter/adapters/filter_map.rs @@ -1,4 +1,5 @@ -use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedFused}; +use crate::iter::adapters::SourceIter; +use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused}; use crate::mem::{ManuallyDrop, MaybeUninit}; use crate::num::NonZero; use crate::ops::{ControlFlow, Try}; @@ -68,7 +69,7 @@ where fn next_chunk( &mut self, ) -> Result<[Self::Item; N], array::IntoIter> { - let mut array: [MaybeUninit; N] = MaybeUninit::uninit_array(); + let mut array: [MaybeUninit; N] = [const { MaybeUninit::uninit() }; N]; struct Guard<'a, T> { array: &'a mut [MaybeUninit], diff --git a/library/core/src/iter/adapters/flatten.rs b/library/core/src/iter/adapters/flatten.rs index 145c9d3dacc84..0023b46031f12 100644 --- a/library/core/src/iter/adapters/flatten.rs +++ b/library/core/src/iter/adapters/flatten.rs @@ -1,13 +1,11 @@ use crate::iter::adapters::SourceIter; use crate::iter::{ - Cloned, Copied, Filter, FilterMap, Fuse, FusedIterator, InPlaceIterable, Map, TrustedFused, - TrustedLen, + Cloned, Copied, Empty, Filter, FilterMap, Fuse, FusedIterator, InPlaceIterable, Map, Once, + OnceWith, TrustedFused, TrustedLen, }; -use crate::iter::{Empty, Once, OnceWith}; use crate::num::NonZero; use crate::ops::{ControlFlow, Try}; -use crate::result; -use crate::{array, fmt, option}; +use crate::{array, fmt, option, result}; /// An iterator that maps each element to an iterator, and yields the elements /// of the produced iterators. diff --git a/library/core/src/iter/adapters/inspect.rs b/library/core/src/iter/adapters/inspect.rs index 1c4656a649a37..0e2a68a503e44 100644 --- a/library/core/src/iter/adapters/inspect.rs +++ b/library/core/src/iter/adapters/inspect.rs @@ -1,5 +1,6 @@ use crate::fmt; -use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedFused}; +use crate::iter::adapters::SourceIter; +use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused}; use crate::num::NonZero; use crate::ops::Try; diff --git a/library/core/src/iter/adapters/map.rs b/library/core/src/iter/adapters/map.rs index 6e163e20d8ec4..007c2d5acc2d0 100644 --- a/library/core/src/iter/adapters/map.rs +++ b/library/core/src/iter/adapters/map.rs @@ -1,7 +1,6 @@ use crate::fmt; -use crate::iter::adapters::{ - zip::try_get_unchecked, SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce, -}; +use crate::iter::adapters::zip::try_get_unchecked; +use crate::iter::adapters::{SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce}; use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused, TrustedLen, UncheckedIterator}; use crate::num::NonZero; use crate::ops::Try; diff --git a/library/core/src/iter/adapters/map_while.rs b/library/core/src/iter/adapters/map_while.rs index 9ad50048c25ea..4e7327938d72a 100644 --- a/library/core/src/iter/adapters/map_while.rs +++ b/library/core/src/iter/adapters/map_while.rs @@ -1,5 +1,6 @@ use crate::fmt; -use crate::iter::{adapters::SourceIter, InPlaceIterable}; +use crate::iter::adapters::SourceIter; +use crate::iter::InPlaceIterable; use crate::num::NonZero; use crate::ops::{ControlFlow, Try}; diff --git a/library/core/src/iter/adapters/map_windows.rs b/library/core/src/iter/adapters/map_windows.rs index 5f39b24583427..cb13023c85c41 100644 --- a/library/core/src/iter/adapters/map_windows.rs +++ b/library/core/src/iter/adapters/map_windows.rs @@ -1,9 +1,6 @@ -use crate::{ - fmt, - iter::FusedIterator, - mem::{self, MaybeUninit}, - ptr, -}; +use crate::iter::FusedIterator; +use crate::mem::{self, MaybeUninit}; +use crate::{fmt, ptr}; /// An iterator over the mapped windows of another iterator. /// @@ -110,7 +107,8 @@ impl MapWindowsInner { impl Buffer { fn try_from_iter(iter: &mut impl Iterator) -> Option { let first_half = crate::array::iter_next_chunk(iter).ok()?; - let buffer = [MaybeUninit::new(first_half).transpose(), MaybeUninit::uninit_array()]; + let buffer = + [MaybeUninit::new(first_half).transpose(), [const { MaybeUninit::uninit() }; N]]; Some(Self { buffer, start: 0 }) } @@ -204,7 +202,7 @@ impl Buffer { impl Clone for Buffer { fn clone(&self) -> Self { let mut buffer = Buffer { - buffer: [MaybeUninit::uninit_array(), MaybeUninit::uninit_array()], + buffer: [[const { MaybeUninit::uninit() }; N], [const { MaybeUninit::uninit() }; N]], start: self.start, }; buffer.as_uninit_array_mut().write(self.as_array_ref().clone()); diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index cc514bd914f14..96158c43318ea 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -28,48 +28,38 @@ mod take; mod take_while; mod zip; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::{ - chain::Chain, cycle::Cycle, enumerate::Enumerate, filter::Filter, filter_map::FilterMap, - flatten::FlatMap, fuse::Fuse, inspect::Inspect, map::Map, peekable::Peekable, rev::Rev, - scan::Scan, skip::Skip, skip_while::SkipWhile, take::Take, take_while::TakeWhile, zip::Zip, -}; - #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] pub use self::array_chunks::ArrayChunks; - #[unstable(feature = "std_internals", issue = "none")] pub use self::by_ref_sized::ByRefSized; - +#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +pub use self::chain::chain; #[stable(feature = "iter_cloned", since = "1.1.0")] pub use self::cloned::Cloned; - -#[stable(feature = "iterator_step_by", since = "1.28.0")] -pub use self::step_by::StepBy; - -#[stable(feature = "iterator_flatten", since = "1.29.0")] -pub use self::flatten::Flatten; - #[stable(feature = "iter_copied", since = "1.36.0")] pub use self::copied::Copied; - +#[stable(feature = "iterator_flatten", since = "1.29.0")] +pub use self::flatten::Flatten; #[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] pub use self::intersperse::{Intersperse, IntersperseWith}; - #[stable(feature = "iter_map_while", since = "1.57.0")] pub use self::map_while::MapWhile; - #[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] pub use self::map_windows::MapWindows; - +#[stable(feature = "iterator_step_by", since = "1.28.0")] +pub use self::step_by::StepBy; +#[stable(feature = "iter_zip", since = "1.59.0")] +pub use self::zip::zip; #[unstable(feature = "trusted_random_access", issue = "none")] pub use self::zip::TrustedRandomAccess; - #[unstable(feature = "trusted_random_access", issue = "none")] pub use self::zip::TrustedRandomAccessNoCoerce; - -#[stable(feature = "iter_zip", since = "1.59.0")] -pub use self::zip::zip; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::{ + chain::Chain, cycle::Cycle, enumerate::Enumerate, filter::Filter, filter_map::FilterMap, + flatten::FlatMap, fuse::Fuse, inspect::Inspect, map::Map, peekable::Peekable, rev::Rev, + scan::Scan, skip::Skip, skip_while::SkipWhile, take::Take, take_while::TakeWhile, zip::Zip, +}; /// This trait provides transitive access to source-stage in an iterator-adapter pipeline /// under the conditions that @@ -156,7 +146,7 @@ pub(crate) struct GenericShunt<'a, I, R> { residual: &'a mut Option, } -/// Process the given iterator as if it yielded a the item's `Try::Output` +/// Process the given iterator as if it yielded the item's `Try::Output` /// type instead. Any `Try::Residual`s encountered will stop the inner iterator /// and be propagated back to the overall result. pub(crate) fn try_process(iter: I, mut f: F) -> ChangeOutputType diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index 65ba42920c93d..a11b73cbe8e2d 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -1,4 +1,5 @@ -use crate::iter::{adapters::SourceIter, FusedIterator, TrustedLen}; +use crate::iter::adapters::SourceIter; +use crate::iter::{FusedIterator, TrustedLen}; use crate::ops::{ControlFlow, Try}; /// An iterator with a `peek()` that returns an optional reference to the next diff --git a/library/core/src/iter/adapters/scan.rs b/library/core/src/iter/adapters/scan.rs index d261a535b183a..7ba7ed2fdd0d5 100644 --- a/library/core/src/iter/adapters/scan.rs +++ b/library/core/src/iter/adapters/scan.rs @@ -1,5 +1,6 @@ use crate::fmt; -use crate::iter::{adapters::SourceIter, InPlaceIterable}; +use crate::iter::adapters::SourceIter; +use crate::iter::InPlaceIterable; use crate::num::NonZero; use crate::ops::{ControlFlow, Try}; diff --git a/library/core/src/iter/adapters/skip.rs b/library/core/src/iter/adapters/skip.rs index f51a2c39b8e28..8ba2e2a8f2dd7 100644 --- a/library/core/src/iter/adapters/skip.rs +++ b/library/core/src/iter/adapters/skip.rs @@ -1,8 +1,8 @@ use crate::intrinsics::unlikely; use crate::iter::adapters::zip::try_get_unchecked; -use crate::iter::TrustedFused; +use crate::iter::adapters::SourceIter; use crate::iter::{ - adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedLen, TrustedRandomAccess, + FusedIterator, InPlaceIterable, TrustedFused, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, }; use crate::num::NonZero; diff --git a/library/core/src/iter/adapters/skip_while.rs b/library/core/src/iter/adapters/skip_while.rs index 8001e6e64713a..8ae453e76fa0d 100644 --- a/library/core/src/iter/adapters/skip_while.rs +++ b/library/core/src/iter/adapters/skip_while.rs @@ -1,5 +1,6 @@ use crate::fmt; -use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedFused}; +use crate::iter::adapters::SourceIter; +use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused}; use crate::num::NonZero; use crate::ops::Try; diff --git a/library/core/src/iter/adapters/step_by.rs b/library/core/src/iter/adapters/step_by.rs index abdf2f415fe55..72eb72a76c66c 100644 --- a/library/core/src/iter/adapters/step_by.rs +++ b/library/core/src/iter/adapters/step_by.rs @@ -1,9 +1,7 @@ -use crate::{ - intrinsics, - iter::{from_fn, TrustedLen, TrustedRandomAccess}, - num::NonZero, - ops::{Range, Try}, -}; +use crate::intrinsics; +use crate::iter::{from_fn, TrustedLen, TrustedRandomAccess}; +use crate::num::NonZero; +use crate::ops::{Range, Try}; /// An iterator for stepping iterators by a custom amount. /// @@ -414,9 +412,9 @@ unsafe impl StepByBackImpl for St /// These only work for unsigned types, and will need to be reworked /// if you want to use it to specialize on signed types. /// -/// Currently these are only implemented for integers up to usize due to -/// correctness issues around ExactSizeIterator impls on 16bit platforms. -/// And since ExactSizeIterator is a prerequisite for backwards iteration +/// Currently these are only implemented for integers up to `usize` due to +/// correctness issues around `ExactSizeIterator` impls on 16bit platforms. +/// And since `ExactSizeIterator` is a prerequisite for backwards iteration /// and we must consistently specialize backwards and forwards iteration /// that makes the situation complicated enough that it's not covered /// for now. diff --git a/library/core/src/iter/adapters/take.rs b/library/core/src/iter/adapters/take.rs index 6870c677b1e07..297dd0acaddc1 100644 --- a/library/core/src/iter/adapters/take.rs +++ b/library/core/src/iter/adapters/take.rs @@ -1,8 +1,6 @@ use crate::cmp; -use crate::iter::{ - adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedFused, TrustedLen, - TrustedRandomAccess, -}; +use crate::iter::adapters::SourceIter; +use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused, TrustedLen, TrustedRandomAccess}; use crate::num::NonZero; use crate::ops::{ControlFlow, Try}; diff --git a/library/core/src/iter/adapters/take_while.rs b/library/core/src/iter/adapters/take_while.rs index d3f09ab356ad8..06028ea98e7fd 100644 --- a/library/core/src/iter/adapters/take_while.rs +++ b/library/core/src/iter/adapters/take_while.rs @@ -1,5 +1,6 @@ use crate::fmt; -use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedFused}; +use crate::iter::adapters::SourceIter; +use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused}; use crate::num::NonZero; use crate::ops::{ControlFlow, Try}; diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs index 2e885f06b5272..0c38811590877 100644 --- a/library/core/src/iter/adapters/zip.rs +++ b/library/core/src/iter/adapters/zip.rs @@ -1,7 +1,8 @@ use crate::cmp; use crate::fmt::{self, Debug}; -use crate::iter::{FusedIterator, TrustedFused}; -use crate::iter::{InPlaceIterable, SourceIter, TrustedLen, UncheckedIterator}; +use crate::iter::{ + FusedIterator, InPlaceIterable, SourceIter, TrustedFused, TrustedLen, UncheckedIterator, +}; use crate::num::NonZero; /// An iterator that iterates two other iterators simultaneously. diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 44fef3e145b78..1f2bf49d2b729 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -380,16 +380,46 @@ macro_rules! impl_fold_via_try_fold { }; } +#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] +pub use self::adapters::chain; +pub(crate) use self::adapters::try_process; +#[stable(feature = "iter_zip", since = "1.59.0")] +pub use self::adapters::zip; +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +pub use self::adapters::ArrayChunks; +#[unstable(feature = "std_internals", issue = "none")] +pub use self::adapters::ByRefSized; +#[stable(feature = "iter_cloned", since = "1.1.0")] +pub use self::adapters::Cloned; +#[stable(feature = "iter_copied", since = "1.36.0")] +pub use self::adapters::Copied; +#[stable(feature = "iterator_flatten", since = "1.29.0")] +pub use self::adapters::Flatten; +#[stable(feature = "iter_map_while", since = "1.57.0")] +pub use self::adapters::MapWhile; +#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +pub use self::adapters::MapWindows; +#[unstable(feature = "inplace_iteration", issue = "none")] +pub use self::adapters::SourceIter; +#[stable(feature = "iterator_step_by", since = "1.28.0")] +pub use self::adapters::StepBy; +#[unstable(feature = "trusted_random_access", issue = "none")] +pub use self::adapters::TrustedRandomAccess; +#[unstable(feature = "trusted_random_access", issue = "none")] +pub use self::adapters::TrustedRandomAccessNoCoerce; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::traits::Iterator; - +pub use self::adapters::{ + Chain, Cycle, Enumerate, Filter, FilterMap, FlatMap, Fuse, Inspect, Map, Peekable, Rev, Scan, + Skip, SkipWhile, Take, TakeWhile, Zip, +}; +#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +pub use self::adapters::{Intersperse, IntersperseWith}; #[unstable( feature = "step_trait", reason = "likely to be replaced by finer-grained traits", issue = "42168" )] pub use self::range::Step; - #[unstable( feature = "iter_from_coroutine", issue = "43122", @@ -412,57 +442,24 @@ pub use self::sources::{repeat_n, RepeatN}; pub use self::sources::{repeat_with, RepeatWith}; #[stable(feature = "iter_successors", since = "1.34.0")] pub use self::sources::{successors, Successors}; - #[stable(feature = "fused", since = "1.26.0")] pub use self::traits::FusedIterator; #[unstable(issue = "none", feature = "inplace_iteration")] pub use self::traits::InPlaceIterable; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::traits::Iterator; #[unstable(issue = "none", feature = "trusted_fused")] pub use self::traits::TrustedFused; #[unstable(feature = "trusted_len", issue = "37572")] pub use self::traits::TrustedLen; #[unstable(feature = "trusted_step", issue = "85731")] pub use self::traits::TrustedStep; +pub(crate) use self::traits::UncheckedIterator; #[stable(feature = "rust1", since = "1.0.0")] pub use self::traits::{ DoubleEndedIterator, ExactSizeIterator, Extend, FromIterator, IntoIterator, Product, Sum, }; -#[stable(feature = "iter_zip", since = "1.59.0")] -pub use self::adapters::zip; -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] -pub use self::adapters::ArrayChunks; -#[unstable(feature = "std_internals", issue = "none")] -pub use self::adapters::ByRefSized; -#[stable(feature = "iter_cloned", since = "1.1.0")] -pub use self::adapters::Cloned; -#[stable(feature = "iter_copied", since = "1.36.0")] -pub use self::adapters::Copied; -#[stable(feature = "iterator_flatten", since = "1.29.0")] -pub use self::adapters::Flatten; -#[stable(feature = "iter_map_while", since = "1.57.0")] -pub use self::adapters::MapWhile; -#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] -pub use self::adapters::MapWindows; -#[unstable(feature = "inplace_iteration", issue = "none")] -pub use self::adapters::SourceIter; -#[stable(feature = "iterator_step_by", since = "1.28.0")] -pub use self::adapters::StepBy; -#[unstable(feature = "trusted_random_access", issue = "none")] -pub use self::adapters::TrustedRandomAccess; -#[unstable(feature = "trusted_random_access", issue = "none")] -pub use self::adapters::TrustedRandomAccessNoCoerce; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::adapters::{ - Chain, Cycle, Enumerate, Filter, FilterMap, FlatMap, Fuse, Inspect, Map, Peekable, Rev, Scan, - Skip, SkipWhile, Take, TakeWhile, Zip, -}; -#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] -pub use self::adapters::{Intersperse, IntersperseWith}; - -pub(crate) use self::adapters::try_process; -pub(crate) use self::traits::UncheckedIterator; - mod adapters; mod range; mod sources; diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 644a169294396..da4f68a0de4fb 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -1,13 +1,12 @@ +use super::{ + FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, TrustedStep, +}; use crate::ascii::Char as AsciiChar; use crate::mem; use crate::net::{Ipv4Addr, Ipv6Addr}; use crate::num::NonZero; use crate::ops::{self, Try}; -use super::{ - FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, TrustedStep, -}; - // Safety: All invariants are upheld. macro_rules! unsafe_impl_trusted_step { ($($type:ty)*) => {$( diff --git a/library/core/src/iter/sources.rs b/library/core/src/iter/sources.rs index 56c1f86079a3a..6a94051b7c7b8 100644 --- a/library/core/src/iter/sources.rs +++ b/library/core/src/iter/sources.rs @@ -8,33 +8,25 @@ mod repeat_n; mod repeat_with; mod successors; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::repeat::{repeat, Repeat}; - #[stable(feature = "iter_empty", since = "1.2.0")] pub use self::empty::{empty, Empty}; - -#[stable(feature = "iter_once", since = "1.2.0")] -pub use self::once::{once, Once}; - -#[unstable(feature = "iter_repeat_n", issue = "104434")] -pub use self::repeat_n::{repeat_n, RepeatN}; - -#[stable(feature = "iterator_repeat_with", since = "1.28.0")] -pub use self::repeat_with::{repeat_with, RepeatWith}; - -#[stable(feature = "iter_from_fn", since = "1.34.0")] -pub use self::from_fn::{from_fn, FromFn}; - #[unstable( feature = "iter_from_coroutine", issue = "43122", reason = "coroutines are unstable" )] pub use self::from_coroutine::from_coroutine; - -#[stable(feature = "iter_successors", since = "1.34.0")] -pub use self::successors::{successors, Successors}; - +#[stable(feature = "iter_from_fn", since = "1.34.0")] +pub use self::from_fn::{from_fn, FromFn}; +#[stable(feature = "iter_once", since = "1.2.0")] +pub use self::once::{once, Once}; #[stable(feature = "iter_once_with", since = "1.43.0")] pub use self::once_with::{once_with, OnceWith}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::repeat::{repeat, Repeat}; +#[unstable(feature = "iter_repeat_n", issue = "104434")] +pub use self::repeat_n::{repeat_n, RepeatN}; +#[stable(feature = "iterator_repeat_with", since = "1.28.0")] +pub use self::repeat_with::{repeat_with, RepeatWith}; +#[stable(feature = "iter_successors", since = "1.34.0")] +pub use self::successors::{successors, Successors}; diff --git a/library/core/src/iter/sources/empty.rs b/library/core/src/iter/sources/empty.rs index 438e046a4dfdc..3c3acceded889 100644 --- a/library/core/src/iter/sources/empty.rs +++ b/library/core/src/iter/sources/empty.rs @@ -1,6 +1,5 @@ -use crate::fmt; use crate::iter::{FusedIterator, TrustedLen}; -use crate::marker; +use crate::{fmt, marker}; /// Creates an iterator that yields nothing. /// diff --git a/library/core/src/iter/sources/repeat.rs b/library/core/src/iter/sources/repeat.rs index 0168b11c7394a..243f938bce2af 100644 --- a/library/core/src/iter/sources/repeat.rs +++ b/library/core/src/iter/sources/repeat.rs @@ -8,11 +8,15 @@ use crate::num::NonZero; /// Infinite iterators like `repeat()` are often used with adapters like /// [`Iterator::take()`], in order to make them finite. /// +/// Use [`str::repeat()`] instead of this function if you just want to repeat +/// a char/string `n`th times. +/// /// If the element type of the iterator you need does not implement `Clone`, /// or if you do not want to keep the repeated element in memory, you can /// instead use the [`repeat_with()`] function. /// /// [`repeat_with()`]: crate::iter::repeat_with +/// [`str::repeat()`]: ../../std/primitive.str.html#method.repeat /// /// # Examples /// diff --git a/library/core/src/iter/sources/repeat_n.rs b/library/core/src/iter/sources/repeat_n.rs index 8224e4b12a0eb..4c4ae39f836ca 100644 --- a/library/core/src/iter/sources/repeat_n.rs +++ b/library/core/src/iter/sources/repeat_n.rs @@ -1,4 +1,4 @@ -use crate::iter::{FusedIterator, TrustedLen}; +use crate::iter::{FusedIterator, TrustedLen, UncheckedIterator}; use crate::mem::ManuallyDrop; use crate::num::NonZero; @@ -114,19 +114,12 @@ impl Iterator for RepeatN { #[inline] fn next(&mut self) -> Option { - if self.count == 0 { - return None; - } - - self.count -= 1; - Some(if self.count == 0 { - // SAFETY: the check above ensured that the count used to be non-zero, - // so element hasn't been dropped yet, and we just lowered the count to - // zero so it won't be dropped later, and thus it's okay to take it here. - unsafe { ManuallyDrop::take(&mut self.element) } + if self.count > 0 { + // SAFETY: Just checked it's not empty + unsafe { Some(self.next_unchecked()) } } else { - A::clone(&self.element) - }) + None + } } #[inline] @@ -193,3 +186,19 @@ impl FusedIterator for RepeatN {} #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for RepeatN {} +#[unstable(feature = "trusted_len_next_unchecked", issue = "37572")] +impl UncheckedIterator for RepeatN { + #[inline] + unsafe fn next_unchecked(&mut self) -> Self::Item { + // SAFETY: The caller promised the iterator isn't empty + self.count = unsafe { self.count.unchecked_sub(1) }; + if self.count == 0 { + // SAFETY: the check above ensured that the count used to be non-zero, + // so element hasn't been dropped yet, and we just lowered the count to + // zero so it won't be dropped later, and thus it's okay to take it here. + unsafe { ManuallyDrop::take(&mut self.element) } + } else { + A::clone(&self.element) + } + } +} diff --git a/library/core/src/iter/sources/successors.rs b/library/core/src/iter/sources/successors.rs index 7f7b2c7756628..36bc4035039e6 100644 --- a/library/core/src/iter/sources/successors.rs +++ b/library/core/src/iter/sources/successors.rs @@ -1,4 +1,5 @@ -use crate::{fmt, iter::FusedIterator}; +use crate::fmt; +use crate::iter::FusedIterator; /// Creates a new iterator where each successive item is computed based on the preceding one. /// diff --git a/library/core/src/iter/traits/accum.rs b/library/core/src/iter/traits/accum.rs index f9c7eb8f9383e..c97cd042ab459 100644 --- a/library/core/src/iter/traits/accum.rs +++ b/library/core/src/iter/traits/accum.rs @@ -15,8 +15,8 @@ use crate::num::Wrapping; label = "value of type `{Self}` cannot be made by summing a `std::iter::Iterator`" )] pub trait Sum: Sized { - /// Method which takes an iterator and generates `Self` from the elements by - /// "summing up" the items. + /// Takes an iterator and generates `Self` from the elements by "summing up" + /// the items. #[stable(feature = "iter_arith_traits", since = "1.12.0")] fn sum>(iter: I) -> Self; } @@ -36,8 +36,8 @@ pub trait Sum: Sized { label = "value of type `{Self}` cannot be made by multiplying all elements from a `std::iter::Iterator`" )] pub trait Product: Sized { - /// Method which takes an iterator and generates `Self` from the elements by - /// multiplying the items. + /// Takes an iterator and generates `Self` from the elements by multiplying + /// the items. #[stable(feature = "iter_arith_traits", since = "1.12.0")] fn product>(iter: I) -> Self; } diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index d9d860c7b6cba..86660f2e375c3 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -1,3 +1,5 @@ +use super::TrustedLen; + /// Conversion from an [`Iterator`]. /// /// By implementing `FromIterator` for a type, you define how it will be @@ -311,8 +313,7 @@ where label = "`{Self}` is not an iterator", message = "`{Self}` is not an iterator" )] -#[cfg_attr(bootstrap, rustc_skip_array_during_method_dispatch)] -#[cfg_attr(not(bootstrap), rustc_skip_during_method_dispatch(array, boxed_slice))] +#[rustc_skip_during_method_dispatch(array, boxed_slice)] #[stable(feature = "rust1", since = "1.0.0")] pub trait IntoIterator { /// The type of the elements being iterated over. @@ -461,6 +462,27 @@ pub trait Extend { fn extend_reserve(&mut self, additional: usize) { let _ = additional; } + + /// Extends a collection with one element, without checking there is enough capacity for it. + /// + /// # Safety + /// + /// **For callers:** This must only be called when we know the collection has enough capacity + /// to contain the new item, for example because we previously called `extend_reserve`. + /// + /// **For implementors:** For a collection to unsafely rely on this method's safety precondition (that is, + /// invoke UB if they are violated), it must implement `extend_reserve` correctly. In other words, + /// callers may assume that if they `extend_reserve`ed enough space they can call this method. + + // This method is for internal usage only. It is only on the trait because of specialization's limitations. + #[unstable(feature = "extend_one_unchecked", issue = "none")] + #[doc(hidden)] + unsafe fn extend_one_unchecked(&mut self, item: A) + where + Self: Sized, + { + self.extend_one(item); + } } #[stable(feature = "extend_for_unit", since = "1.28.0")] @@ -500,33 +522,102 @@ where fn extend>(&mut self, into_iter: T) { let (a, b) = self; let iter = into_iter.into_iter(); + SpecTupleExtend::extend(iter, a, b); + } + + fn extend_one(&mut self, item: (A, B)) { + self.0.extend_one(item.0); + self.1.extend_one(item.1); + } + + fn extend_reserve(&mut self, additional: usize) { + self.0.extend_reserve(additional); + self.1.extend_reserve(additional); + } + + unsafe fn extend_one_unchecked(&mut self, item: (A, B)) { + // SAFETY: Those are our safety preconditions, and we correctly forward `extend_reserve`. + unsafe { + self.0.extend_one_unchecked(item.0); + self.1.extend_one_unchecked(item.1); + } + } +} + +fn default_extend_tuple( + iter: impl Iterator, + a: &mut ExtendA, + b: &mut ExtendB, +) where + ExtendA: Extend, + ExtendB: Extend, +{ + fn extend<'a, A, B>( + a: &'a mut impl Extend, + b: &'a mut impl Extend, + ) -> impl FnMut((), (A, B)) + 'a { + move |(), (t, u)| { + a.extend_one(t); + b.extend_one(u); + } + } + + let (lower_bound, _) = iter.size_hint(); + if lower_bound > 0 { + a.extend_reserve(lower_bound); + b.extend_reserve(lower_bound); + } + + iter.fold((), extend(a, b)); +} + +trait SpecTupleExtend { + fn extend(self, a: &mut A, b: &mut B); +} +impl SpecTupleExtend for Iter +where + ExtendA: Extend, + ExtendB: Extend, + Iter: Iterator, +{ + default fn extend(self, a: &mut ExtendA, b: &mut ExtendB) { + default_extend_tuple(self, a, b); + } +} + +impl SpecTupleExtend for Iter +where + ExtendA: Extend, + ExtendB: Extend, + Iter: TrustedLen, +{ + fn extend(self, a: &mut ExtendA, b: &mut ExtendB) { fn extend<'a, A, B>( a: &'a mut impl Extend, b: &'a mut impl Extend, ) -> impl FnMut((), (A, B)) + 'a { - move |(), (t, u)| { - a.extend_one(t); - b.extend_one(u); + // SAFETY: We reserve enough space for the `size_hint`, and the iterator is `TrustedLen` + // so its `size_hint` is exact. + move |(), (t, u)| unsafe { + a.extend_one_unchecked(t); + b.extend_one_unchecked(u); } } - let (lower_bound, _) = iter.size_hint(); + let (lower_bound, upper_bound) = self.size_hint(); + + if upper_bound.is_none() { + // We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway. + default_extend_tuple(self, a, b); + return; + } + if lower_bound > 0 { a.extend_reserve(lower_bound); b.extend_reserve(lower_bound); } - iter.fold((), extend(a, b)); - } - - fn extend_one(&mut self, item: (A, B)) { - self.0.extend_one(item.0); - self.1.extend_one(item.1); - } - - fn extend_reserve(&mut self, additional: usize) { - self.0.extend_reserve(additional); - self.1.extend_reserve(additional); + self.fold((), extend(a, b)); } } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index cee99e28b5a97..50a2d952e5b36 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -1,19 +1,14 @@ +use super::super::{ + try_process, ArrayChunks, ByRefSized, Chain, Cloned, Copied, Cycle, Enumerate, Filter, + FilterMap, FlatMap, Flatten, Fuse, Inspect, Intersperse, IntersperseWith, Map, MapWhile, + MapWindows, Peekable, Product, Rev, Scan, Skip, SkipWhile, StepBy, Sum, Take, TakeWhile, + TrustedRandomAccessNoCoerce, Zip, +}; use crate::array; use crate::cmp::{self, Ordering}; use crate::num::NonZero; use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try}; -use super::super::try_process; -use super::super::ByRefSized; -use super::super::TrustedRandomAccessNoCoerce; -use super::super::{ArrayChunks, Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; -use super::super::{FlatMap, Flatten}; -use super::super::{ - Inspect, Map, MapWhile, MapWindows, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, - TakeWhile, -}; -use super::super::{Intersperse, IntersperseWith, Product, Sum, Zip}; - fn _assert_is_object_safe(_: &dyn Iterator) {} /// A trait for dealing with iterators. @@ -823,7 +818,7 @@ pub trait Iterator { /// /// Given an element the closure must return `true` or `false`. The returned /// iterator will yield only the elements for which the closure returns - /// true. + /// `true`. /// /// # Examples /// @@ -2080,8 +2075,7 @@ pub trait Iterator { fn try_collect(&mut self) -> ChangeOutputType where Self: Sized, - ::Item: Try, - <::Item as Try>::Residual: Residual, + Self::Item: Try>, B: FromIterator<::Output>, { try_process(ByRefSized(self), |i| i.collect()) @@ -2689,12 +2683,13 @@ pub trait Iterator { #[inline] #[unstable(feature = "iterator_try_reduce", reason = "new API", issue = "87053")] #[rustc_do_not_const_check] - fn try_reduce(&mut self, f: F) -> ChangeOutputType> + fn try_reduce( + &mut self, + f: impl FnMut(Self::Item, Self::Item) -> R, + ) -> ChangeOutputType> where Self: Sized, - F: FnMut(Self::Item, Self::Item) -> R, - R: Try, - R::Residual: Residual>, + R: Try>>, { let first = match self.next() { Some(i) => i, @@ -2956,12 +2951,13 @@ pub trait Iterator { #[inline] #[unstable(feature = "try_find", reason = "new API", issue = "63178")] #[rustc_do_not_const_check] - fn try_find(&mut self, f: F) -> ChangeOutputType> + fn try_find( + &mut self, + f: impl FnMut(&Self::Item) -> R, + ) -> ChangeOutputType> where Self: Sized, - F: FnMut(&Self::Item) -> R, - R: Try, - R::Residual: Residual>, + R: Try>>, { #[inline] fn check( @@ -3950,8 +3946,6 @@ pub trait Iterator { /// # Examples /// /// ``` - /// #![feature(is_sorted)] - /// /// assert!([1, 2, 2, 9].iter().is_sorted()); /// assert!(![1, 3, 2, 4].iter().is_sorted()); /// assert!([0].iter().is_sorted()); @@ -3959,7 +3953,7 @@ pub trait Iterator { /// assert!(![0.0, 1.0, f32::NAN].iter().is_sorted()); /// ``` #[inline] - #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] + #[stable(feature = "is_sorted", since = "CURRENT_RUSTC_VERSION")] #[rustc_do_not_const_check] fn is_sorted(self) -> bool where @@ -3977,8 +3971,6 @@ pub trait Iterator { /// # Examples /// /// ``` - /// #![feature(is_sorted)] - /// /// assert!([1, 2, 2, 9].iter().is_sorted_by(|a, b| a <= b)); /// assert!(![1, 2, 2, 9].iter().is_sorted_by(|a, b| a < b)); /// @@ -3988,7 +3980,7 @@ pub trait Iterator { /// assert!(std::iter::empty::().is_sorted_by(|a, b| false)); /// assert!(std::iter::empty::().is_sorted_by(|a, b| true)); /// ``` - #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] + #[stable(feature = "is_sorted", since = "CURRENT_RUSTC_VERSION")] #[rustc_do_not_const_check] fn is_sorted_by(mut self, compare: F) -> bool where @@ -4029,13 +4021,11 @@ pub trait Iterator { /// # Examples /// /// ``` - /// #![feature(is_sorted)] - /// /// assert!(["c", "bb", "aaa"].iter().is_sorted_by_key(|s| s.len())); /// assert!(![-2i32, -1, 0, 3].iter().is_sorted_by_key(|n| n.abs())); /// ``` #[inline] - #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] + #[stable(feature = "is_sorted", since = "CURRENT_RUSTC_VERSION")] #[rustc_do_not_const_check] fn is_sorted_by_key(self, f: F) -> bool where diff --git a/library/core/src/iter/traits/mod.rs b/library/core/src/iter/traits/mod.rs index d4c9cc4b16037..b330e9ffe21ac 100644 --- a/library/core/src/iter/traits/mod.rs +++ b/library/core/src/iter/traits/mod.rs @@ -6,6 +6,13 @@ mod iterator; mod marker; mod unchecked_iterator; +#[unstable(issue = "none", feature = "inplace_iteration")] +pub use self::marker::InPlaceIterable; +#[unstable(issue = "none", feature = "trusted_fused")] +pub use self::marker::TrustedFused; +#[unstable(feature = "trusted_step", issue = "85731")] +pub use self::marker::TrustedStep; +pub(crate) use self::unchecked_iterator::UncheckedIterator; #[stable(feature = "rust1", since = "1.0.0")] pub use self::{ accum::{Product, Sum}, @@ -15,12 +22,3 @@ pub use self::{ iterator::Iterator, marker::{FusedIterator, TrustedLen}, }; - -#[unstable(issue = "none", feature = "inplace_iteration")] -pub use self::marker::InPlaceIterable; -#[unstable(issue = "none", feature = "trusted_fused")] -pub use self::marker::TrustedFused; -#[unstable(feature = "trusted_step", issue = "85731")] -pub use self::marker::TrustedStep; - -pub(crate) use self::unchecked_iterator::UncheckedIterator; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 7626d6d4a3cc8..2cc89ddc53942 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -34,12 +34,9 @@ //! Rust user code is to call the functions provided by this library instead (such as //! `ptr::copy`). //! -//! * `rust_begin_panic` - This function takes four arguments, a -//! `fmt::Arguments`, a `&'static str`, and two `u32`'s. These four arguments -//! dictate the panic message, the file at which panic was invoked, and the -//! line and column inside the file. It is up to consumers of this core +//! * Panic handler - This function takes one argument, a `&panic::PanicInfo`. It is up to consumers of this core //! library to define this panic function; it is only required to never -//! return. This requires a `lang` attribute named `panic_impl`. +//! return. You should mark your implementation using `#[panic_handler]`. //! //! * `rust_eh_personality` - is used by the failure mechanisms of the //! compiler. This is often mapped to GCC's personality function, but crates @@ -106,9 +103,11 @@ #![deny(ffi_unwind_calls)] // Do not check link redundancy on bootstraping phase #![allow(rustdoc::redundant_explicit_links)] +#![warn(rustdoc::unescaped_backticks)] // // Library features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(offset_of_nested))] #![feature(array_ptr_get)] #![feature(asm_experimental_arch)] #![feature(char_indices_offset)] @@ -122,7 +121,6 @@ #![feature(const_bigint_helper_methods)] #![feature(const_black_box)] #![feature(const_cell_into_inner)] -#![feature(const_char_from_u32_unchecked)] #![feature(const_eval_select)] #![feature(const_exact_div)] #![feature(const_float_bits_conv)] @@ -130,9 +128,7 @@ #![feature(const_fmt_arguments_new)] #![feature(const_hash)] #![feature(const_heap)] -#![feature(const_hint_assert_unchecked)] #![feature(const_index_range_slice_index)] -#![feature(const_int_from_str)] #![feature(const_intrinsic_copy)] #![feature(const_intrinsic_forget)] #![feature(const_ipv4)] @@ -140,7 +136,6 @@ #![feature(const_likely)] #![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_maybe_uninit_assume_init)] -#![feature(const_maybe_uninit_uninit_array)] #![feature(const_nonnull_new)] #![feature(const_num_midpoint)] #![feature(const_option)] @@ -169,18 +164,15 @@ #![feature(const_ub_checks)] #![feature(const_unicode_case_lookup)] #![feature(const_unsafecell_get_mut)] -#![feature(const_waker)] #![feature(coverage_attribute)] +#![feature(do_not_recommend)] #![feature(duration_consts_float)] #![feature(internal_impls_macro)] #![feature(ip)] -#![feature(ip_bits)] #![feature(is_ascii_octdigit)] #![feature(isqrt)] #![feature(link_cfg)] -#![feature(maybe_uninit_uninit_array)] #![feature(offset_of_enum)] -#![feature(offset_of_nested)] #![feature(panic_internals)] #![feature(ptr_alignment_type)] #![feature(ptr_metadata)] @@ -191,6 +183,7 @@ #![feature(str_split_remainder)] #![feature(strict_provenance)] #![feature(ub_checks)] +#![feature(unchecked_neg)] #![feature(unchecked_shifts)] #![feature(utf16_extra)] #![feature(utf16_extra_const)] @@ -205,23 +198,19 @@ #![feature(allow_internal_unstable)] #![feature(asm_const)] #![feature(auto_traits)] -#![feature(c_unwind)] #![feature(cfg_sanitize)] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_has_atomic_equal_alignment)] -#![feature(const_closures)] #![feature(const_fn_floating_point_arithmetic)] #![feature(const_for)] #![feature(const_mut_refs)] #![feature(const_precise_live_drops)] #![feature(const_refs_to_cell)] -#![feature(const_trait_impl)] #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(doc_cfg)] #![feature(doc_cfg_hide)] #![feature(doc_notable_trait)] -#![feature(effects)] #![feature(extern_types)] #![feature(f128)] #![feature(f16)] @@ -235,6 +224,7 @@ #![feature(let_chains)] #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] +#![feature(marker_trait_attr)] #![feature(min_exhaustive_patterns)] #![feature(min_specialization)] #![feature(multiple_supertrait_upcastable)] @@ -255,7 +245,6 @@ #![feature(trait_alias)] #![feature(transparent_unions)] #![feature(try_blocks)] -#![feature(type_alias_impl_trait)] #![feature(unboxed_closures)] #![feature(unsized_fn_params)] #![feature(with_negative_coherence)] @@ -271,9 +260,11 @@ #![feature(powerpc_target_feature)] #![feature(riscv_target_feature)] #![feature(rtm_target_feature)] +#![feature(sha512_sm_x86)] #![feature(sse4a_target_feature)] #![feature(tbm_target_feature)] #![feature(wasm_target_feature)] +#![feature(x86_amx_intrinsics)] // tidy-alphabetical-end // allow using `core::` in intra-doc links @@ -403,6 +394,8 @@ pub mod panicking; #[unstable(feature = "core_pattern_types", issue = "none")] pub mod pat; pub mod pin; +#[unstable(feature = "new_range_api", issue = "125687")] +pub mod range; pub mod result; pub mod sync; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 2ddedfa37fe27..ac51a40d9f478 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -14,6 +14,12 @@ macro_rules! panic { /// Asserts that two expressions are equal to each other (using [`PartialEq`]). /// +/// Assertions are always checked in both debug and release builds, and cannot +/// be disabled. See [`debug_assert_eq!`] for assertions that are disabled in +/// release builds by default. +/// +/// [`debug_assert_eq!`]: crate::debug_assert_eq +/// /// On panic, this macro will print the values of the expressions with their /// debug representations. /// @@ -64,6 +70,12 @@ macro_rules! assert_eq { /// Asserts that two expressions are not equal to each other (using [`PartialEq`]). /// +/// Assertions are always checked in both debug and release builds, and cannot +/// be disabled. See [`debug_assert_ne!`] for assertions that are disabled in +/// release builds by default. +/// +/// [`debug_assert_ne!`]: crate::debug_assert_ne +/// /// On panic, this macro will print the values of the expressions with their /// debug representations. /// @@ -122,6 +134,12 @@ macro_rules! assert_ne { /// optional if guard can be used to add additional checks that must be true for the matched value, /// otherwise this macro will panic. /// +/// Assertions are always checked in both debug and release builds, and cannot +/// be disabled. See [`debug_assert_matches!`] for assertions that are disabled in +/// release builds by default. +/// +/// [`debug_assert_matches!`]: crate::assert_matches::debug_assert_matches +/// /// On panic, this macro will print the value of the expression with its debug representation. /// /// Like [`assert!`], this macro has a second form, where a custom panic message can be provided. @@ -633,7 +651,7 @@ macro_rules! write { }; } -/// Write formatted data into a buffer, with a newline appended. +/// Writes formatted data into a buffer, with a newline appended. /// /// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone /// (no additional CARRIAGE RETURN (`\r`/`U+000D`). @@ -1569,7 +1587,12 @@ pub(crate) mod builtin { #[rustc_builtin_macro] #[macro_export] #[rustc_diagnostic_item = "assert_macro"] - #[allow_internal_unstable(panic_internals, edition_panic, generic_assert_internals)] + #[allow_internal_unstable( + core_intrinsics, + panic_internals, + edition_panic, + generic_assert_internals + )] macro_rules! assert { ($cond:expr $(,)?) => {{ /* compiler built-in */ }}; ($cond:expr, $($arg:tt)+) => {{ /* compiler built-in */ }}; diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 1d073a6d649b8..6a83ec2eb1e0e 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -9,8 +9,7 @@ use crate::cell::UnsafeCell; use crate::cmp; use crate::fmt::Debug; -use crate::hash::Hash; -use crate::hash::Hasher; +use crate::hash::{Hash, Hasher}; /// Implements a given marker trait for multiple types at the same time. /// @@ -42,6 +41,8 @@ use crate::hash::Hasher; /// } /// ``` #[unstable(feature = "internal_impls_macro", issue = "none")] +// Allow implementations of `UnsizedConstParamTy` even though std cannot use that feature. +#[allow_internal_unstable(unsized_const_params)] macro marker_impls { ( $(#[$($meta:tt)*])* $Trait:ident for $({$($bounds:tt)*})? $T:ty $(, $($rest:tt)*)? ) => { $(#[$($meta)*])* impl< $($($bounds)*)? > $Trait for $T {} @@ -869,7 +870,7 @@ marker_impls! { /// /// *However*, you cannot use [`mem::replace`] on `!Unpin` data which is *pinned* by being wrapped /// inside a [`Pin`] pointing at it. This is because you cannot (safely) use a -/// [`Pin`] to get an `&mut T` to its pointee value, which you would need to call +/// [`Pin`] to get a `&mut T` to its pointee value, which you would need to call /// [`mem::replace`], and *that* is what makes this system work. /// /// So this, for example, can only be done on types implementing `Unpin`: @@ -944,7 +945,6 @@ marker_impls! { #[lang = "destruct"] #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)] #[rustc_deny_explicit_impl(implement_via_object = false)] -#[const_trait] pub trait Destruct {} /// A marker for tuple types. @@ -976,35 +976,74 @@ pub trait PointerLike {} /// that all fields are also `ConstParamTy`, which implies that recursively, all fields /// are `StructuralPartialEq`. #[lang = "const_param_ty"] -#[unstable(feature = "adt_const_params", issue = "95174")] +#[unstable(feature = "unsized_const_params", issue = "95174")] #[diagnostic::on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] #[allow(multiple_supertrait_upcastable)] -pub trait ConstParamTy: StructuralPartialEq + Eq {} +// We name this differently than the derive macro so that the `adt_const_params` can +// be used independently of `unsized_const_params` without requiring a full path +// to the derive macro every time it is used. This should be renamed on stabilization. +pub trait ConstParamTy_: UnsizedConstParamTy + StructuralPartialEq + Eq {} /// Derive macro generating an impl of the trait `ConstParamTy`. #[rustc_builtin_macro] +#[allow_internal_unstable(unsized_const_params)] #[unstable(feature = "adt_const_params", issue = "95174")] pub macro ConstParamTy($item:item) { /* compiler built-in */ } +#[cfg_attr(not(bootstrap), lang = "unsized_const_param_ty")] +#[unstable(feature = "unsized_const_params", issue = "95174")] +#[diagnostic::on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] +/// A marker for types which can be used as types of `const` generic parameters. +/// +/// Equivalent to [`ConstParamTy_`] except that this is used by +/// the `unsized_const_params` to allow for fake unstable impls. +pub trait UnsizedConstParamTy: StructuralPartialEq + Eq {} + +/// Derive macro generating an impl of the trait `ConstParamTy`. +#[cfg(not(bootstrap))] +#[cfg_attr(not(bootstrap), rustc_builtin_macro)] +#[cfg_attr(not(bootstrap), allow_internal_unstable(unsized_const_params))] +#[cfg_attr(not(bootstrap), unstable(feature = "unsized_const_params", issue = "95174"))] +pub macro UnsizedConstParamTy($item:item) { + /* compiler built-in */ +} + // FIXME(adt_const_params): handle `ty::FnDef`/`ty::Closure` marker_impls! { #[unstable(feature = "adt_const_params", issue = "95174")] - ConstParamTy for + ConstParamTy_ for usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, bool, char, - str /* Technically requires `[u8]: ConstParamTy` */, - {T: ConstParamTy, const N: usize} [T; N], - {T: ConstParamTy} [T], - {T: ?Sized + ConstParamTy} &T, + (), + {T: ConstParamTy_, const N: usize} [T; N], +} +#[cfg(bootstrap)] +marker_impls! { + #[unstable(feature = "adt_const_params", issue = "95174")] + ConstParamTy_ for + str, + {T: ConstParamTy_} [T], + {T: ConstParamTy_ + ?Sized} &T, } -// FIXME(adt_const_params): Add to marker_impls call above once not in bootstrap -#[unstable(feature = "adt_const_params", issue = "95174")] -impl ConstParamTy for () {} +marker_impls! { + #[unstable(feature = "unsized_const_params", issue = "95174")] + UnsizedConstParamTy for + usize, u8, u16, u32, u64, u128, + isize, i8, i16, i32, i64, i128, + bool, + char, + (), + {T: UnsizedConstParamTy, const N: usize} [T; N], + + str, + {T: UnsizedConstParamTy} [T], + {T: UnsizedConstParamTy + ?Sized} &T, +} /// A common trait implemented by all function pointers. #[unstable( @@ -1019,3 +1058,56 @@ pub trait FnPtr: Copy + Clone { #[lang = "fn_ptr_addr"] fn addr(self) -> *const (); } + +/// Derive macro generating impls of traits related to smart pointers. +#[rustc_builtin_macro] +#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)] +#[unstable(feature = "derive_smart_pointer", issue = "123430")] +pub macro SmartPointer($item:item) { + /* compiler built-in */ +} + +// Support traits and types for the desugaring of const traits and +// `~const` bounds. Not supposed to be used by anything other than +// the compiler. +#[doc(hidden)] +#[unstable( + feature = "effect_types", + issue = "none", + reason = "internal module for implementing effects" +)] +#[allow(missing_debug_implementations)] // these unit structs don't need `Debug` impls. +pub mod effects { + #[lang = "EffectsNoRuntime"] + pub struct NoRuntime; + #[lang = "EffectsMaybe"] + pub struct Maybe; + #[lang = "EffectsRuntime"] + pub struct Runtime; + + #[lang = "EffectsCompat"] + pub trait Compat<#[rustc_runtime] const RUNTIME: bool> {} + + impl Compat for NoRuntime {} + impl Compat for Runtime {} + impl<#[rustc_runtime] const RUNTIME: bool> Compat for Maybe {} + + #[lang = "EffectsTyCompat"] + #[marker] + pub trait TyCompat {} + + impl TyCompat for T {} + impl TyCompat for Maybe {} + impl TyCompat for T {} + + #[lang = "EffectsIntersection"] + pub trait Intersection { + #[lang = "EffectsIntersectionOutput"] + type Output: ?Sized; + } + + // FIXME(effects): remove this after next trait solver lands + impl Intersection for () { + type Output = Maybe; + } +} diff --git a/library/core/src/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs index e0c3b9f3b51da..00c837041b697 100644 --- a/library/core/src/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -62,6 +62,9 @@ impl ManuallyDrop { /// x.truncate(5); // You can still safely operate on the value /// assert_eq!(*x, "Hello"); /// // But `Drop` will not be run here + /// # // FIXME(https://github.com/rust-lang/miri/issues/3670): + /// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak. + /// # let _ = ManuallyDrop::into_inner(x); /// ``` #[must_use = "if you don't need the wrapper, you can use `mem::forget` instead"] #[stable(feature = "manually_drop", since = "1.20.0")] @@ -115,10 +118,12 @@ impl ManuallyDrop { } impl ManuallyDrop { - /// Manually drops the contained value. This is exactly equivalent to calling - /// [`ptr::drop_in_place`] with a pointer to the contained value. As such, unless - /// the contained value is a packed struct, the destructor will be called in-place - /// without moving the value, and thus can be used to safely drop [pinned] data. + /// Manually drops the contained value. + /// + /// This is exactly equivalent to calling [`ptr::drop_in_place`] with a + /// pointer to the contained value. As such, unless the contained value is a + /// packed struct, the destructor will be called in-place without moving the + /// value, and thus can be used to safely drop [pinned] data. /// /// If you have ownership of the value, you can use [`ManuallyDrop::into_inner`] instead. /// diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 026e21586d403..f920ab1792daf 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1,9 +1,6 @@ use crate::any::type_name; -use crate::fmt; -use crate::intrinsics; use crate::mem::{self, ManuallyDrop}; -use crate::ptr; -use crate::slice; +use crate::{fmt, intrinsics, ptr, slice}; /// A wrapper type to construct uninitialized instances of `T`. /// @@ -120,12 +117,8 @@ use crate::slice; /// use std::mem::{self, MaybeUninit}; /// /// let data = { -/// // Create an uninitialized array of `MaybeUninit`. The `assume_init` is -/// // safe because the type we are claiming to have initialized here is a -/// // bunch of `MaybeUninit`s, which do not require initialization. -/// let mut data: [MaybeUninit>; 1000] = unsafe { -/// MaybeUninit::uninit().assume_init() -/// }; +/// // Create an uninitialized array of `MaybeUninit`. +/// let mut data: [MaybeUninit>; 1000] = [const { MaybeUninit::uninit() }; 1000]; /// /// // Dropping a `MaybeUninit` does nothing, so if there is a panic during this loop, /// // we have a memory leak, but there is no memory safety issue. @@ -147,10 +140,8 @@ use crate::slice; /// ``` /// use std::mem::MaybeUninit; /// -/// // Create an uninitialized array of `MaybeUninit`. The `assume_init` is -/// // safe because the type we are claiming to have initialized here is a -/// // bunch of `MaybeUninit`s, which do not require initialization. -/// let mut data: [MaybeUninit; 1000] = unsafe { MaybeUninit::uninit().assume_init() }; +/// // Create an uninitialized array of `MaybeUninit`. +/// let mut data: [MaybeUninit; 1000] = [const { MaybeUninit::uninit() }; 1000]; /// // Count the number of elements we have assigned. /// let mut data_len: usize = 0; /// @@ -280,6 +271,8 @@ impl MaybeUninit { /// use std::mem::MaybeUninit; /// /// let v: MaybeUninit> = MaybeUninit::new(vec![42]); + /// # // Prevent leaks for Miri + /// # unsafe { let _ = MaybeUninit::assume_init(v); } /// ``` /// /// [`assume_init`]: MaybeUninit::assume_init @@ -314,7 +307,7 @@ impl MaybeUninit { MaybeUninit { uninit: () } } - /// Create a new array of `MaybeUninit` items, in an uninitialized state. + /// Creates a new array of `MaybeUninit` items, in an uninitialized state. /// /// Note: in a future Rust version this method may become unnecessary /// when Rust allows @@ -348,8 +341,7 @@ impl MaybeUninit { #[must_use] #[inline(always)] pub const fn uninit_array() -> [Self; N] { - // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid. - unsafe { MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init() } + [const { MaybeUninit::uninit() }; N] } /// Creates a new `MaybeUninit` in an uninitialized state, with the memory being @@ -453,6 +445,9 @@ impl MaybeUninit { /// let mut x = MaybeUninit::::uninit(); /// /// x.write("Hello".to_string()); + /// # // FIXME(https://github.com/rust-lang/miri/issues/3670): + /// # // use -Zmiri-disable-leak-check instead of unleaking in tests meant to leak. + /// # unsafe { MaybeUninit::assume_init_drop(&mut x); } /// // This leaks the contained string: /// x.write("hello".to_string()); /// // x is initialized now: @@ -513,6 +508,8 @@ impl MaybeUninit { /// // Create a reference into the `MaybeUninit`. This is okay because we initialized it. /// let x_vec = unsafe { &*x.as_ptr() }; /// assert_eq!(x_vec.len(), 3); + /// # // Prevent leaks for Miri + /// # unsafe { MaybeUninit::assume_init_drop(&mut x); } /// ``` /// /// *Incorrect* usage of this method: @@ -552,6 +549,8 @@ impl MaybeUninit { /// let x_vec = unsafe { &mut *x.as_mut_ptr() }; /// x_vec.push(3); /// assert_eq!(x_vec.len(), 4); + /// # // Prevent leaks for Miri + /// # unsafe { MaybeUninit::assume_init_drop(&mut x); } /// ``` /// /// *Incorrect* usage of this method: @@ -753,6 +752,8 @@ impl MaybeUninit { /// use std::mem::MaybeUninit; /// /// let mut x = MaybeUninit::>::uninit(); + /// # let mut x_mu = x; + /// # let mut x = &mut x_mu; /// // Initialize `x`: /// x.write(vec![1, 2, 3]); /// // Now that our `MaybeUninit<_>` is known to be initialized, it is okay to @@ -762,6 +763,8 @@ impl MaybeUninit { /// x.assume_init_ref() /// }; /// assert_eq!(x, &vec![1, 2, 3]); + /// # // Prevent leaks for Miri + /// # unsafe { MaybeUninit::assume_init_drop(&mut x_mu); } /// ``` /// /// ### *Incorrect* usages of this method: @@ -924,11 +927,10 @@ impl MaybeUninit { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_uninit_array)] /// #![feature(maybe_uninit_array_assume_init)] /// use std::mem::MaybeUninit; /// - /// let mut array: [MaybeUninit; 3] = MaybeUninit::uninit_array(); + /// let mut array: [MaybeUninit; 3] = [MaybeUninit::uninit(); 3]; /// array[0].write(0); /// array[1].write(1); /// array[2].write(2); @@ -1096,6 +1098,8 @@ impl MaybeUninit { /// let init = MaybeUninit::clone_from_slice(&mut dst, &src); /// /// assert_eq!(init, src); + /// # // Prevent leaks for Miri + /// # unsafe { std::ptr::drop_in_place(init); } /// ``` /// /// ``` diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index cf3f819cd5c15..3c36545b8d421 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -5,13 +5,8 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::clone; -use crate::cmp; -use crate::fmt; -use crate::hash; -use crate::intrinsics; use crate::marker::DiscriminantKind; -use crate::ptr; +use crate::{clone, cmp, fmt, hash, intrinsics, ptr}; #[cfg(kani)] use crate::kani; @@ -362,6 +357,12 @@ pub const fn size_of_val(val: &T) -> usize { /// - a [slice], then the length of the slice tail must be an initialized /// integer, and the size of the *entire value* /// (dynamic tail length + statically sized prefix) must fit in `isize`. +/// For the special case where the dynamic tail length is 0, this function +/// is safe to call. +// NOTE: the reason this is safe is that if an overflow were to occur already with size 0, +// then we would stop compilation as even the "statically known" part of the type would +// already be too big (or the call may be in dead code and optimized away, but then it +// doesn't matter). /// - a [trait object], then the vtable part of the pointer must point /// to a valid vtable acquired by an unsizing coercion, and the size /// of the *entire value* (dynamic tail length + statically sized prefix) @@ -509,6 +510,8 @@ pub const fn align_of_val(val: &T) -> usize { /// - a [slice], then the length of the slice tail must be an initialized /// integer, and the size of the *entire value* /// (dynamic tail length + statically sized prefix) must fit in `isize`. +/// For the special case where the dynamic tail length is 0, this function +/// is safe to call. /// - a [trait object], then the vtable part of the pointer must point /// to a valid vtable acquired by an unsizing coercion, and the size /// of the *entire value* (dynamic tail length + statically sized prefix) @@ -1271,6 +1274,20 @@ impl SizedTypeProperties for T {} /// // ^^^ error[E0616]: field `private` of struct `Struct` is private /// ``` /// +/// Only [`Sized`] fields are supported, but the container may be unsized: +/// ``` +/// # use core::mem; +/// #[repr(C)] +/// pub struct Struct { +/// a: u8, +/// b: [u8], +/// } +/// +/// assert_eq!(mem::offset_of!(Struct, a), 0); // OK +/// // assert_eq!(mem::offset_of!(Struct, b), 1); +/// // ^^^ error[E0277]: doesn't have a size known at compile-time +/// ``` +/// /// Note that type layout is, in general, [subject to change and /// platform-specific](https://doc.rust-lang.org/reference/type-layout.html). If /// layout stability is required, consider using an [explicit `repr` attribute]. @@ -1309,7 +1326,8 @@ impl SizedTypeProperties for T {} /// # Examples /// /// ``` -/// #![feature(offset_of_enum, offset_of_nested)] +/// # #![cfg_attr(bootstrap, feature(offset_of_nested))] +/// #![feature(offset_of_enum)] /// /// use std::mem; /// #[repr(C)] diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index 827426b235839..ea73c5b80ba44 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -1,4 +1,4 @@ -use crate::marker::ConstParamTy; +use crate::marker::{ConstParamTy_, UnsizedConstParamTy}; /// Are values of a type transmutable into values of another type? /// @@ -39,7 +39,9 @@ pub struct Assume { } #[unstable(feature = "transmutability", issue = "99571")] -impl ConstParamTy for Assume {} +impl ConstParamTy_ for Assume {} +#[unstable(feature = "transmutability", issue = "99571")] +impl UnsizedConstParamTy for Assume {} impl Assume { /// Do not assume that *you* have ensured any safety properties are met. diff --git a/library/core/src/net/display_buffer.rs b/library/core/src/net/display_buffer.rs index b7e778605fc0a..bab84a97308b3 100644 --- a/library/core/src/net/display_buffer.rs +++ b/library/core/src/net/display_buffer.rs @@ -1,6 +1,5 @@ -use crate::fmt; use crate::mem::MaybeUninit; -use crate::str; +use crate::{fmt, str}; /// Used for slow path in `Display` implementations when alignment is required. pub struct DisplayBuffer { @@ -11,7 +10,7 @@ pub struct DisplayBuffer { impl DisplayBuffer { #[inline] pub const fn new() -> Self { - Self { buf: MaybeUninit::uninit_array(), len: 0 } + Self { buf: [MaybeUninit::uninit(); SIZE], len: 0 } } #[inline] diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index 959c3289affbf..3e036b88128c7 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -1,11 +1,10 @@ +use super::display_buffer::DisplayBuffer; use crate::cmp::Ordering; use crate::fmt::{self, Write}; use crate::iter; use crate::mem::transmute; use crate::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not}; -use super::display_buffer::DisplayBuffer; - /// An IP address, either IPv4 or IPv6. /// /// This enum can contain either an [`Ipv4Addr`] or an [`Ipv6Addr`], see their @@ -406,8 +405,8 @@ impl IpAddr { matches!(self, IpAddr::V6(_)) } - /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped IPv6 addresses, otherwise it - /// returns `self` as-is. + /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped IPv6 + /// address, otherwise returns `self` as-is. /// /// # Examples /// @@ -460,12 +459,11 @@ impl Ipv4Addr { /// # Examples /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv4Addr; /// /// assert_eq!(Ipv4Addr::BITS, 32); /// ``` - #[unstable(feature = "ip_bits", issue = "113744")] + #[stable(feature = "ip_bits", since = "1.80.0")] pub const BITS: u32 = 32; /// Converts an IPv4 address into a `u32` representation using native byte order. @@ -479,7 +477,6 @@ impl Ipv4Addr { /// # Examples /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv4Addr; /// /// let addr = Ipv4Addr::new(0x12, 0x34, 0x56, 0x78); @@ -487,7 +484,6 @@ impl Ipv4Addr { /// ``` /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv4Addr; /// /// let addr = Ipv4Addr::new(0x12, 0x34, 0x56, 0x78); @@ -495,8 +491,8 @@ impl Ipv4Addr { /// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x00), Ipv4Addr::from_bits(addr_bits)); /// /// ``` - #[rustc_const_unstable(feature = "ip_bits", issue = "113744")] - #[unstable(feature = "ip_bits", issue = "113744")] + #[rustc_const_stable(feature = "ip_bits", since = "1.80.0")] + #[stable(feature = "ip_bits", since = "1.80.0")] #[must_use] #[inline] pub const fn to_bits(self) -> u32 { @@ -510,14 +506,13 @@ impl Ipv4Addr { /// # Examples /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv4Addr; /// /// let addr = Ipv4Addr::from(0x12345678); /// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78), addr); /// ``` - #[rustc_const_unstable(feature = "ip_bits", issue = "113744")] - #[unstable(feature = "ip_bits", issue = "113744")] + #[rustc_const_stable(feature = "ip_bits", since = "1.80.0")] + #[stable(feature = "ip_bits", since = "1.80.0")] #[must_use] #[inline] pub const fn from_bits(bits: u32) -> Ipv4Addr { @@ -553,7 +548,7 @@ impl Ipv4Addr { #[stable(feature = "ip_constructors", since = "1.30.0")] pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0); - /// An IPv4 address representing the broadcast address: `255.255.255.255` + /// An IPv4 address representing the broadcast address: `255.255.255.255`. /// /// # Examples /// @@ -690,10 +685,10 @@ impl Ipv4Addr { /// Returns [`true`] if the address appears to be globally reachable /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. /// - /// Most IPv4 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. + /// Whether or not an address is practically reachable will depend on your + /// network configuration. Most IPv4 addresses are globally reachable, unless + /// they are specifically defined as *not* globally reachable. /// /// Non-exhaustive list of notable addresses that are not globally reachable: /// @@ -806,8 +801,10 @@ impl Ipv4Addr { } /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for - /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` - /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. + /// network devices benchmarking. + /// + /// This range is defined in [IETF RFC 2544] as `192.18.0.0` through + /// `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. /// /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 /// [errata 423]: https://www.rfc-editor.org/errata/eid423 @@ -831,10 +828,12 @@ impl Ipv4Addr { self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 } - /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] - /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the - /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since - /// it is obviously not reserved for future use. + /// Returns [`true`] if this address is reserved by IANA for future use. + /// + /// [IETF RFC 1112] defines the block of reserved addresses as `240.0.0.0/4`. + /// This range normally includes the broadcast address `255.255.255.255`, but + /// this implementation explicitly excludes it, since it is obviously not + /// reserved for future use. /// /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 /// @@ -1238,12 +1237,11 @@ impl Ipv6Addr { /// # Examples /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv6Addr; /// /// assert_eq!(Ipv6Addr::BITS, 128); /// ``` - #[unstable(feature = "ip_bits", issue = "113744")] + #[stable(feature = "ip_bits", since = "1.80.0")] pub const BITS: u32 = 128; /// Converts an IPv6 address into a `u128` representation using native byte order. @@ -1257,7 +1255,6 @@ impl Ipv6Addr { /// # Examples /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv6Addr; /// /// let addr = Ipv6Addr::new( @@ -1268,7 +1265,6 @@ impl Ipv6Addr { /// ``` /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv6Addr; /// /// let addr = Ipv6Addr::new( @@ -1284,8 +1280,8 @@ impl Ipv6Addr { /// Ipv6Addr::from_bits(addr_bits)); /// /// ``` - #[rustc_const_unstable(feature = "ip_bits", issue = "113744")] - #[unstable(feature = "ip_bits", issue = "113744")] + #[rustc_const_stable(feature = "ip_bits", since = "1.80.0")] + #[stable(feature = "ip_bits", since = "1.80.0")] #[must_use] #[inline] pub const fn to_bits(self) -> u128 { @@ -1299,7 +1295,6 @@ impl Ipv6Addr { /// # Examples /// /// ``` - /// #![feature(ip_bits)] /// use std::net::Ipv6Addr; /// /// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128); @@ -1310,8 +1305,8 @@ impl Ipv6Addr { /// ), /// addr); /// ``` - #[rustc_const_unstable(feature = "ip_bits", issue = "113744")] - #[unstable(feature = "ip_bits", issue = "113744")] + #[rustc_const_stable(feature = "ip_bits", since = "1.80.0")] + #[stable(feature = "ip_bits", since = "1.80.0")] #[must_use] #[inline] pub const fn from_bits(bits: u128) -> Ipv6Addr { @@ -1336,7 +1331,7 @@ impl Ipv6Addr { #[stable(feature = "ip_constructors", since = "1.30.0")] pub const LOCALHOST: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); - /// An IPv6 address representing the unspecified address: `::` + /// An IPv6 address representing the unspecified address: `::`. /// /// This corresponds to constant `IN6ADDR_ANY_INIT` or `in6addr_any` in other languages. /// @@ -1432,10 +1427,10 @@ impl Ipv6Addr { /// Returns [`true`] if the address appears to be globally reachable /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. /// - /// Most IPv6 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. + /// Whether or not an address is practically reachable will depend on your + /// network configuration. Most IPv6 addresses are globally reachable, unless + /// they are specifically defined as *not* globally reachable. /// /// Non-exhaustive list of notable addresses that are not globally reachable: /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) @@ -1887,8 +1882,8 @@ impl Ipv6Addr { } } - /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped addresses, otherwise it - /// returns self wrapped in an `IpAddr::V6`. + /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped address, + /// otherwise returns self wrapped in an `IpAddr::V6`. /// /// # Examples /// @@ -1927,7 +1922,7 @@ impl Ipv6Addr { } } -/// Write an Ipv6Addr, conforming to the canonical style described by +/// Writes an Ipv6Addr, conforming to the canonical style described by /// [RFC 5952](https://tools.ietf.org/html/rfc5952). #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for Ipv6Addr { @@ -1970,7 +1965,7 @@ impl fmt::Display for Ipv6Addr { longest }; - /// Write a colon-separated part of the address + /// Writes a colon-separated part of the address. #[inline] fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result { if let Some((first, tail)) = chunk.split_first() { diff --git a/library/core/src/net/parser.rs b/library/core/src/net/parser.rs index deea821244859..a8ec71f0dd801 100644 --- a/library/core/src/net/parser.rs +++ b/library/core/src/net/parser.rs @@ -68,7 +68,7 @@ impl<'a> Parser<'a> { self.state.first().map(|&b| char::from(b)) } - /// Read the next character from the input + /// Reads the next character from the input fn read_char(&mut self) -> Option { self.state.split_first().map(|(&b, tail)| { self.state = tail; @@ -77,7 +77,7 @@ impl<'a> Parser<'a> { } #[must_use] - /// Read the next character from the input if it matches the target. + /// Reads the next character from the input if it matches the target. fn read_given_char(&mut self, target: char) -> Option<()> { self.read_atomically(|p| { p.read_char().and_then(|c| if c == target { Some(()) } else { None }) @@ -165,7 +165,7 @@ impl<'a> Parser<'a> { } } - /// Read an IPv4 address. + /// Reads an IPv4 address. fn read_ipv4_addr(&mut self) -> Option { self.read_atomically(|p| { let mut groups = [0; 4]; @@ -182,7 +182,7 @@ impl<'a> Parser<'a> { }) } - /// Read an IPv6 Address. + /// Reads an IPv6 address. fn read_ipv6_addr(&mut self) -> Option { /// Read a chunk of an IPv6 address into `groups`. Returns the number /// of groups read, along with a bool indicating if an embedded @@ -249,12 +249,12 @@ impl<'a> Parser<'a> { }) } - /// Read an IP Address, either IPv4 or IPv6. + /// Reads an IP address, either IPv4 or IPv6. fn read_ip_addr(&mut self) -> Option { self.read_ipv4_addr().map(IpAddr::V4).or_else(move || self.read_ipv6_addr().map(IpAddr::V6)) } - /// Read a `:` followed by a port in base 10. + /// Reads a `:` followed by a port in base 10. fn read_port(&mut self) -> Option { self.read_atomically(|p| { p.read_given_char(':')?; @@ -262,7 +262,7 @@ impl<'a> Parser<'a> { }) } - /// Read a `%` followed by a scope ID in base 10. + /// Reads a `%` followed by a scope ID in base 10. fn read_scope_id(&mut self) -> Option { self.read_atomically(|p| { p.read_given_char('%')?; @@ -270,7 +270,7 @@ impl<'a> Parser<'a> { }) } - /// Read an IPv4 address with a port. + /// Reads an IPv4 address with a port. fn read_socket_addr_v4(&mut self) -> Option { self.read_atomically(|p| { let ip = p.read_ipv4_addr()?; @@ -279,7 +279,7 @@ impl<'a> Parser<'a> { }) } - /// Read an IPv6 address with a port. + /// Reads an IPv6 address with a port. fn read_socket_addr_v6(&mut self) -> Option { self.read_atomically(|p| { p.read_given_char('[')?; @@ -292,7 +292,7 @@ impl<'a> Parser<'a> { }) } - /// Read an IP address with a port + /// Reads an IP address with a port. fn read_socket_addr(&mut self) -> Option { self.read_socket_addr_v4() .map(SocketAddr::V4) diff --git a/library/core/src/net/socket_addr.rs b/library/core/src/net/socket_addr.rs index c24d8f5519504..4e339172b682f 100644 --- a/library/core/src/net/socket_addr.rs +++ b/library/core/src/net/socket_addr.rs @@ -1,8 +1,7 @@ +use super::display_buffer::DisplayBuffer; use crate::fmt::{self, Write}; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use super::display_buffer::DisplayBuffer; - /// An internet socket address, either IPv4 or IPv6. /// /// Internet socket addresses consist of an [IP address], a 16-bit port number, as well diff --git a/library/core/src/num/bignum.rs b/library/core/src/num/bignum.rs index d2a21b6b38260..2a47c89e2aee2 100644 --- a/library/core/src/num/bignum.rs +++ b/library/core/src/num/bignum.rs @@ -145,8 +145,7 @@ macro_rules! define_bignum { /// Adds `other` to itself and returns its own mutable reference. pub fn add<'a>(&'a mut self, other: &$name) -> &'a mut $name { - use crate::cmp; - use crate::iter; + use crate::{cmp, iter}; let mut sz = cmp::max(self.size, other.size); let mut carry = false; @@ -181,8 +180,7 @@ macro_rules! define_bignum { /// Subtracts `other` from itself and returns its own mutable reference. pub fn sub<'a>(&'a mut self, other: &$name) -> &'a mut $name { - use crate::cmp; - use crate::iter; + use crate::{cmp, iter}; let sz = cmp::max(self.size, other.size); let mut noborrow = true; diff --git a/library/core/src/num/dec2flt/common.rs b/library/core/src/num/dec2flt/common.rs index 11a626485191c..4dadf406ae8c7 100644 --- a/library/core/src/num/dec2flt/common.rs +++ b/library/core/src/num/dec2flt/common.rs @@ -2,10 +2,10 @@ /// Helper methods to process immutable bytes. pub(crate) trait ByteSlice { - /// Read 8 bytes as a 64-bit integer in little-endian order. + /// Reads 8 bytes as a 64-bit integer in little-endian order. fn read_u64(&self) -> u64; - /// Write a 64-bit integer as 8 bytes in little-endian order. + /// Writes a 64-bit integer as 8 bytes in little-endian order. fn write_u64(&mut self, value: u64); /// Calculate the offset of a slice from another. @@ -39,9 +39,7 @@ impl ByteSlice for [u8] { fn parse_digits(&self, mut func: impl FnMut(u8)) -> &Self { let mut s = self; - // FIXME: Can't use s.split_first() here yet, - // see https://github.com/rust-lang/rust/issues/109328 - while let [c, s_next @ ..] = s { + while let Some((c, s_next)) = s.split_first() { let c = c.wrapping_sub(b'0'); if c < 10 { func(c); diff --git a/library/core/src/num/dec2flt/float.rs b/library/core/src/num/dec2flt/float.rs index 1c9d68999d6f8..da57aa9a546af 100644 --- a/library/core/src/num/dec2flt/float.rs +++ b/library/core/src/num/dec2flt/float.rs @@ -81,7 +81,7 @@ pub trait RawFloat: // Maximum mantissa for the fast-path (`1 << 53` for f64). const MAX_MANTISSA_FAST_PATH: u64 = 2_u64 << Self::MANTISSA_EXPLICIT_BITS; - /// Convert integer into float through an as cast. + /// Converts integer into float through an as cast. /// This is only called in the fast-path algorithm, and therefore /// will not lose precision, since the value will always have /// only if the value is <= Self::MAX_MANTISSA_FAST_PATH. @@ -90,7 +90,7 @@ pub trait RawFloat: /// Performs a raw transmutation from an integer. fn from_u64_bits(v: u64) -> Self; - /// Get a small power-of-ten for fast-path multiplication. + /// Gets a small power-of-ten for fast-path multiplication. fn pow10_fast_path(exponent: usize) -> Self; /// Returns the category that this number falls into. diff --git a/library/core/src/num/dec2flt/lemire.rs b/library/core/src/num/dec2flt/lemire.rs index 3bc052df7a6c1..01642e1b1112a 100644 --- a/library/core/src/num/dec2flt/lemire.rs +++ b/library/core/src/num/dec2flt/lemire.rs @@ -157,7 +157,7 @@ fn compute_product_approx(q: i64, w: u64, precision: usize) -> (u64, u64) { // Need to do a second multiplication to get better precision // for the lower product. This will always be exact // where q is < 55, since 5^55 < 2^128. If this wraps, - // then we need to need to round up the hi product. + // then we need to round up the hi product. let (_, second_hi) = full_multiplication(w, hi5); first_lo = first_lo.wrapping_add(second_hi); if second_hi > first_lo { diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs index a4bc8b1c9b0c3..87bfd0d256634 100644 --- a/library/core/src/num/dec2flt/mod.rs +++ b/library/core/src/num/dec2flt/mod.rs @@ -75,15 +75,14 @@ issue = "none" )] -use crate::error::Error; -use crate::fmt; -use crate::str::FromStr; - use self::common::BiasedFp; use self::float::RawFloat; use self::lemire::compute_float; use self::parse::{parse_inf_nan, parse_number}; use self::slow::parse_long_mantissa; +use crate::error::Error; +use crate::fmt; +use crate::str::FromStr; mod common; mod decimal; @@ -250,8 +249,10 @@ pub fn dec2flt(s: &str) -> Result { None => return Err(pfe_invalid()), }; num.negative = negative; - if let Some(value) = num.try_fast_path::() { - return Ok(value); + if !cfg!(feature = "optimize_for_size") { + if let Some(value) = num.try_fast_path::() { + return Ok(value); + } } // If significant digits were truncated, then we can have rounding error diff --git a/library/core/src/num/dec2flt/parse.rs b/library/core/src/num/dec2flt/parse.rs index b0a23835c5bd4..975bb8ad6bc1f 100644 --- a/library/core/src/num/dec2flt/parse.rs +++ b/library/core/src/num/dec2flt/parse.rs @@ -51,9 +51,7 @@ fn try_parse_19digits(s_ref: &mut &[u8], x: &mut u64) { let mut s = *s_ref; while *x < MIN_19DIGIT_INT { - // FIXME: Can't use s.split_first() here yet, - // see https://github.com/rust-lang/rust/issues/109328 - if let [c, s_next @ ..] = s { + if let Some((c, s_next)) = s.split_first() { let digit = c.wrapping_sub(b'0'); if digit < 10 { diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index a2d7e6f7b0754..b8e22a8aef955 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -113,7 +113,7 @@ pub enum IntErrorKind { impl ParseIntError { /// Outputs the detailed cause of parsing an integer failing. #[must_use] - #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "int_error_matching", since = "1.55.0")] pub const fn kind(&self) -> &IntErrorKind { &self.kind diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 9362dc8765492..6a24748fd9e87 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -11,7 +11,11 @@ #![unstable(feature = "f128", issue = "116909")] +use crate::convert::FloatToInt; +#[cfg(not(test))] +use crate::intrinsics; use crate::mem; +use crate::num::FpCategory; /// Basic mathematical constants. #[unstable(feature = "f128", issue = "116909")] @@ -68,6 +72,13 @@ pub mod consts { pub const FRAC_1_SQRT_PI: f128 = 0.564189583547756286948079451560772585844050629328998856844086_f128; + /// 1/sqrt(2π) + #[doc(alias = "FRAC_1_SQRT_TAU")] + #[unstable(feature = "f128", issue = "116909")] + // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + pub const FRAC_1_SQRT_2PI: f128 = + 0.398942280401432677939946059934381868475858631164934657665926_f128; + /// 2/π #[unstable(feature = "f128", issue = "116909")] pub const FRAC_2_PI: f128 = 0.636619772367581343075535053490057448137838582961825794990669_f128; @@ -159,7 +170,7 @@ impl f128 { /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon /// [`MANTISSA_DIGITS`]: f128::MANTISSA_DIGITS #[unstable(feature = "f128", issue = "116909")] - pub const EPSILON: f128 = 1.92592994438723585305597794258492731e-34_f128; + pub const EPSILON: f128 = 1.92592994438723585305597794258492732e-34_f128; /// Smallest finite `f128` value. /// @@ -167,7 +178,7 @@ impl f128 { /// /// [`MAX`]: f128::MAX #[unstable(feature = "f128", issue = "116909")] - pub const MIN: f128 = -1.18973149535723176508575932662800701e+4932_f128; + pub const MIN: f128 = -1.18973149535723176508575932662800702e+4932_f128; /// Smallest positive normal `f128` value. /// /// Equal to 2[`MIN_EXP`] − 1. @@ -183,7 +194,7 @@ impl f128 { /// [`MANTISSA_DIGITS`]: f128::MANTISSA_DIGITS /// [`MAX_EXP`]: f128::MAX_EXP #[unstable(feature = "f128", issue = "116909")] - pub const MAX: f128 = 1.18973149535723176508575932662800701e+4932_f128; + pub const MAX: f128 = 1.18973149535723176508575932662800702e+4932_f128; /// One greater than the minimum possible normal power of 2 exponent. /// @@ -213,7 +224,58 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] pub const MAX_10_EXP: i32 = 4_932; + /// Not a Number (NaN). + /// + /// Note that IEEE 754 doesn't define just a single NaN value; + /// a plethora of bit patterns are considered to be NaN. + /// Furthermore, the standard makes a difference + /// between a "signaling" and a "quiet" NaN, + /// and allows inspecting its "payload" (the unspecified bits in the bit pattern). + /// This constant isn't guaranteed to equal to any specific NaN bitpattern, + /// and the stability of its representation over Rust versions + /// and target platforms isn't guaranteed. + #[allow(clippy::eq_op)] + #[rustc_diagnostic_item = "f128_nan"] + #[unstable(feature = "f128", issue = "116909")] + pub const NAN: f128 = 0.0_f128 / 0.0_f128; + + /// Infinity (∞). + #[unstable(feature = "f128", issue = "116909")] + pub const INFINITY: f128 = 1.0_f128 / 0.0_f128; + + /// Negative infinity (−∞). + #[unstable(feature = "f128", issue = "116909")] + pub const NEG_INFINITY: f128 = -1.0_f128 / 0.0_f128; + + /// Sign bit + pub(crate) const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; + + /// Exponent mask + pub(crate) const EXP_MASK: u128 = 0x7fff_0000_0000_0000_0000_0000_0000_0000; + + /// Mantissa mask + pub(crate) const MAN_MASK: u128 = 0x0000_ffff_ffff_ffff_ffff_ffff_ffff_ffff; + + /// Minimum representable positive value (min subnormal) + const TINY_BITS: u128 = 0x1; + + /// Minimum representable negative value (min negative subnormal) + const NEG_TINY_BITS: u128 = Self::TINY_BITS | Self::SIGN_MASK; + /// Returns `true` if this value is NaN. + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `unordtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let nan = f128::NAN; + /// let f = 7.0_f128; + /// + /// assert!(nan.is_nan()); + /// assert!(!f.is_nan()); + /// # } + /// ``` #[inline] #[must_use] #[unstable(feature = "f128", issue = "116909")] @@ -222,12 +284,191 @@ impl f128 { self != self } + // FIXME(#50145): `abs` is publicly unavailable in core due to + // concerns about portability, so this implementation is for + // private use internally. + #[inline] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub(crate) const fn abs_private(self) -> f128 { + // SAFETY: This transmutation is fine. Probably. For the reasons std is using it. + unsafe { + mem::transmute::(mem::transmute::(self) & !Self::SIGN_MASK) + } + } + + /// Returns `true` if this value is positive infinity or negative infinity, and + /// `false` otherwise. + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `eqtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let f = 7.0f128; + /// let inf = f128::INFINITY; + /// let neg_inf = f128::NEG_INFINITY; + /// let nan = f128::NAN; + /// + /// assert!(!f.is_infinite()); + /// assert!(!nan.is_infinite()); + /// + /// assert!(inf.is_infinite()); + /// assert!(neg_inf.is_infinite()); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn is_infinite(self) -> bool { + (self == f128::INFINITY) | (self == f128::NEG_INFINITY) + } + + /// Returns `true` if this number is neither infinite nor NaN. + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `lttf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let f = 7.0f128; + /// let inf: f128 = f128::INFINITY; + /// let neg_inf: f128 = f128::NEG_INFINITY; + /// let nan: f128 = f128::NAN; + /// + /// assert!(f.is_finite()); + /// + /// assert!(!nan.is_finite()); + /// assert!(!inf.is_finite()); + /// assert!(!neg_inf.is_finite()); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn is_finite(self) -> bool { + // There's no need to handle NaN separately: if self is NaN, + // the comparison is not true, exactly as desired. + self.abs_private() < Self::INFINITY + } + + /// Returns `true` if the number is [subnormal]. + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `eqtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let min = f128::MIN_POSITIVE; // 3.362103143e-4932f128 + /// let max = f128::MAX; + /// let lower_than_min = 1.0e-4960_f128; + /// let zero = 0.0_f128; + /// + /// assert!(!min.is_subnormal()); + /// assert!(!max.is_subnormal()); + /// + /// assert!(!zero.is_subnormal()); + /// assert!(!f128::NAN.is_subnormal()); + /// assert!(!f128::INFINITY.is_subnormal()); + /// // Values between `0` and `min` are Subnormal. + /// assert!(lower_than_min.is_subnormal()); + /// # } + /// ``` + /// + /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn is_subnormal(self) -> bool { + matches!(self.classify(), FpCategory::Subnormal) + } + + /// Returns `true` if the number is neither zero, infinite, [subnormal], or NaN. + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `eqtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let min = f128::MIN_POSITIVE; // 3.362103143e-4932f128 + /// let max = f128::MAX; + /// let lower_than_min = 1.0e-4960_f128; + /// let zero = 0.0_f128; + /// + /// assert!(min.is_normal()); + /// assert!(max.is_normal()); + /// + /// assert!(!zero.is_normal()); + /// assert!(!f128::NAN.is_normal()); + /// assert!(!f128::INFINITY.is_normal()); + /// // Values between `0` and `min` are Subnormal. + /// assert!(!lower_than_min.is_normal()); + /// # } + /// ``` + /// + /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn is_normal(self) -> bool { + matches!(self.classify(), FpCategory::Normal) + } + + /// Returns the floating point category of the number. If only one property + /// is going to be tested, it is generally faster to use the specific + /// predicate instead. + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `eqtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// use std::num::FpCategory; + /// + /// let num = 12.4_f128; + /// let inf = f128::INFINITY; + /// + /// assert_eq!(num.classify(), FpCategory::Normal); + /// assert_eq!(inf.classify(), FpCategory::Infinite); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn classify(self) -> FpCategory { + // Other float types cannot use a bitwise classify because they may suffer a variety + // of errors if the backend chooses to cast to different float types (x87). `f128` cannot + // fit into any other float types so this is not a concern, and we rely on bit patterns. + + // SAFETY: POD bitcast, same as in `to_bits`. + let bits = unsafe { mem::transmute::(self) }; + Self::classify_bits(bits) + } + + /// This operates on bits, and only bits, so it can ignore concerns about weird FPUs. + /// FIXME(jubilee): In a just world, this would be the entire impl for classify, + /// plus a transmute. We do not live in a just world, but we can make it more so. + #[inline] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + const fn classify_bits(b: u128) -> FpCategory { + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { + (0, Self::EXP_MASK) => FpCategory::Infinite, + (_, Self::EXP_MASK) => FpCategory::Nan, + (0, 0) => FpCategory::Zero, + (_, 0) => FpCategory::Subnormal, + _ => FpCategory::Normal, + } + } + /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with /// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that /// the bit pattern of NaNs are conserved over arithmetic operations, the result of /// `is_sign_positive` on a NaN might produce an unexpected result in some cases. - /// See [explanation of NaN as a special value](f32) for more info. + /// See [explanation of NaN as a special value](f128) for more info. /// /// ``` /// #![feature(f128)] @@ -250,7 +491,7 @@ impl f128 { /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that /// the bit pattern of NaNs are conserved over arithmetic operations, the result of /// `is_sign_negative` on a NaN might produce an unexpected result in some cases. - /// See [explanation of NaN as a special value](f32) for more info. + /// See [explanation of NaN as a special value](f128) for more info. /// /// ``` /// #![feature(f128)] @@ -271,6 +512,217 @@ impl f128 { (self.to_bits() & (1 << 127)) != 0 } + /// Returns the least number greater than `self`. + /// + /// Let `TINY` be the smallest representable positive `f128`. Then, + /// - if `self.is_nan()`, this returns `self`; + /// - if `self` is [`NEG_INFINITY`], this returns [`MIN`]; + /// - if `self` is `-TINY`, this returns -0.0; + /// - if `self` is -0.0 or +0.0, this returns `TINY`; + /// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`]; + /// - otherwise the unique least value greater than `self` is returned. + /// + /// The identity `x.next_up() == -(-x).next_down()` holds for all non-NaN `x`. When `x` + /// is finite `x == x.next_up().next_down()` also holds. + /// + /// ```rust + /// #![feature(f128)] + /// #![feature(float_next_up_down)] + /// # // FIXME(f16_f128): remove when `eqtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// // f128::EPSILON is the difference between 1.0 and the next number up. + /// assert_eq!(1.0f128.next_up(), 1.0 + f128::EPSILON); + /// // But not for most numbers. + /// assert!(0.1f128.next_up() < 0.1 + f128::EPSILON); + /// assert_eq!(4611686018427387904f128.next_up(), 4611686018427387904.000000000000001); + /// # } + /// ``` + /// + /// [`NEG_INFINITY`]: Self::NEG_INFINITY + /// [`INFINITY`]: Self::INFINITY + /// [`MIN`]: Self::MIN + /// [`MAX`]: Self::MAX + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_next_up_down", issue = "91399")] + pub fn next_up(self) -> Self { + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. + let bits = self.to_bits(); + if self.is_nan() || bits == Self::INFINITY.to_bits() { + return self; + } + + let abs = bits & !Self::SIGN_MASK; + let next_bits = if abs == 0 { + Self::TINY_BITS + } else if bits == abs { + bits + 1 + } else { + bits - 1 + }; + Self::from_bits(next_bits) + } + + /// Returns the greatest number less than `self`. + /// + /// Let `TINY` be the smallest representable positive `f128`. Then, + /// - if `self.is_nan()`, this returns `self`; + /// - if `self` is [`INFINITY`], this returns [`MAX`]; + /// - if `self` is `TINY`, this returns 0.0; + /// - if `self` is -0.0 or +0.0, this returns `-TINY`; + /// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`]; + /// - otherwise the unique greatest value less than `self` is returned. + /// + /// The identity `x.next_down() == -(-x).next_up()` holds for all non-NaN `x`. When `x` + /// is finite `x == x.next_down().next_up()` also holds. + /// + /// ```rust + /// #![feature(f128)] + /// #![feature(float_next_up_down)] + /// # // FIXME(f16_f128): remove when `eqtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// // Clamp value into range [0, 1). + /// let clamped = x.clamp(0.0, 1.0f128.next_down()); + /// assert!(clamped < 1.0); + /// assert_eq!(clamped.next_up(), 1.0); + /// # } + /// ``` + /// + /// [`NEG_INFINITY`]: Self::NEG_INFINITY + /// [`INFINITY`]: Self::INFINITY + /// [`MIN`]: Self::MIN + /// [`MAX`]: Self::MAX + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_next_up_down", issue = "91399")] + pub fn next_down(self) -> Self { + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. + let bits = self.to_bits(); + if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() { + return self; + } + + let abs = bits & !Self::SIGN_MASK; + let next_bits = if abs == 0 { + Self::NEG_TINY_BITS + } else if bits == abs { + bits - 1 + } else { + bits + 1 + }; + Self::from_bits(next_bits) + } + + /// Takes the reciprocal (inverse) of a number, `1/x`. + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `eqtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.recip() - (1.0 / x)).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn recip(self) -> Self { + 1.0 / self + } + + /// Converts radians to degrees. + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `eqtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let angle = std::f128::consts::PI; + /// + /// let abs_difference = (angle.to_degrees() - 180.0).abs(); + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_degrees(self) -> Self { + // Use a literal for better precision. + const PIS_IN_180: f128 = 57.2957795130823208767981548141051703324054724665643215491602_f128; + self * PIS_IN_180 + } + + /// Converts degrees to radians. + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `eqtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let angle = 180.0f128; + /// + /// let abs_difference = (angle.to_radians() - std::f128::consts::PI).abs(); + /// + /// assert!(abs_difference <= 1e-30); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_radians(self) -> f128 { + // Use a literal for better precision. + const RADS_PER_DEG: f128 = + 0.0174532925199432957692369076848861271344287188854172545609719_f128; + self * RADS_PER_DEG + } + + /// Rounds toward zero and converts to any primitive integer type, + /// assuming that the value is finite and fits in that type. + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `float*itf` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let value = 4.6_f128; + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, 4); + /// + /// let value = -128.9_f128; + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, i8::MIN); + /// # } + /// ``` + /// + /// # Safety + /// + /// The value must: + /// + /// * Not be `NaN` + /// * Not be infinite + /// * Be representable in the return type `Int`, after truncating off its fractional part + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub unsafe fn to_int_unchecked(self) -> Int + where + Self: FloatToInt, + { + // SAFETY: the caller must uphold the safety contract for + // `FloatToInt::to_int_unchecked`. + unsafe { FloatToInt::::to_int_unchecked(self) } + } + /// Raw transmutation to `u128`. /// /// This is currently identical to `transmute::(self)` on all platforms. @@ -280,14 +732,62 @@ impl f128 { /// /// Note that this function is distinct from `as` casting, which attempts to /// preserve the *numeric* value, and not the bitwise value. + /// + /// ``` + /// #![feature(f128)] + /// + /// # // FIXME(f16_f128): enable this once const casting works + /// # // assert_ne!((1f128).to_bits(), 1f128 as u128); // to_bits() is not casting! + /// assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); + /// ``` #[inline] #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] #[must_use = "this returns the result of the operation, without modifying the original"] - pub fn to_bits(self) -> u128 { - // SAFETY: `u128` is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - unsafe { mem::transmute(self) } + pub const fn to_bits(self) -> u128 { + // SAFETY: `u128` is a plain old datatype so we can always transmute to it. + // ...sorta. + // + // It turns out that at runtime, it is possible for a floating point number + // to be subject to a floating point mode that alters nonzero subnormal numbers + // to zero on reads and writes, aka "denormals are zero" and "flush to zero". + // + // And, of course evaluating to a NaN value is fairly nondeterministic. + // More precisely: when NaN should be returned is knowable, but which NaN? + // So far that's defined by a combination of LLVM and the CPU, not Rust. + // This function, however, allows observing the bitstring of a NaN, + // thus introspection on CTFE. + // + // In order to preserve, at least for the moment, const-to-runtime equivalence, + // we reject any of these possible situations from happening. + #[inline] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + const fn ct_f128_to_u128(ct: f128) -> u128 { + // FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but that + // is not available on all platforms (needs `netf2` and `unordtf2`). So classify + // the bits instead. + + // SAFETY: this is a POD transmutation + let bits = unsafe { mem::transmute::(ct) }; + match f128::classify_bits(bits) { + FpCategory::Nan => { + panic!("const-eval error: cannot use f128::to_bits on a NaN") + } + FpCategory::Subnormal => { + panic!("const-eval error: cannot use f128::to_bits on a subnormal number") + } + FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => bits, + } + } + + #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491 + fn rt_f128_to_u128(x: f128) -> u128 { + // SAFETY: `u128` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute(x) } + } + intrinsics::const_eval_select((self,), ct_f128_to_u128, rt_f128_to_u128) } /// Raw transmutation from `u128`. @@ -319,13 +819,379 @@ impl f128 { /// /// Note that this function is distinct from `as` casting, which attempts to /// preserve the *numeric* value, and not the bitwise value. + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `eqtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let v = f128::from_bits(0x40029000000000000000000000000000); + /// assert_eq!(v, 12.5); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + pub const fn from_bits(v: u128) -> Self { + // It turns out the safety issues with sNaN were overblown! Hooray! + // SAFETY: `u128` is a plain old datatype so we can always transmute from it + // ...sorta. + // + // It turns out that at runtime, it is possible for a floating point number + // to be subject to floating point modes that alter nonzero subnormal numbers + // to zero on reads and writes, aka "denormals are zero" and "flush to zero". + // This is not a problem usually, but at least one tier2 platform for Rust + // actually exhibits this behavior by default: thumbv7neon + // aka "the Neon FPU in AArch32 state" + // + // And, of course evaluating to a NaN value is fairly nondeterministic. + // More precisely: when NaN should be returned is knowable, but which NaN? + // So far that's defined by a combination of LLVM and the CPU, not Rust. + // This function, however, allows observing the bitstring of a NaN, + // thus introspection on CTFE. + // + // In order to preserve, at least for the moment, const-to-runtime equivalence, + // reject any of these possible situations from happening. + #[inline] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + const fn ct_u128_to_f128(ct: u128) -> f128 { + match f128::classify_bits(ct) { + FpCategory::Subnormal => { + panic!("const-eval error: cannot use f128::from_bits on a subnormal number") + } + FpCategory::Nan => { + panic!("const-eval error: cannot use f128::from_bits on NaN") + } + FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => { + // SAFETY: It's not a frumious number + unsafe { mem::transmute::(ct) } + } + } + } + + #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491 + fn rt_u128_to_f128(x: u128) -> f128 { + // SAFETY: `u128` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute(x) } + } + intrinsics::const_eval_select((v,), ct_u128_to_f128, rt_u128_to_f128) + } + + /// Returns the memory representation of this floating point number as a byte array in + /// big-endian (network) byte order. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// + /// let bytes = 12.5f128.to_be_bytes(); + /// assert_eq!( + /// bytes, + /// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// ); + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 16] { + self.to_bits().to_be_bytes() + } + + /// Returns the memory representation of this floating point number as a byte array in + /// little-endian byte order. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// + /// let bytes = 12.5f128.to_le_bytes(); + /// assert_eq!( + /// bytes, + /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40] + /// ); + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 16] { + self.to_bits().to_le_bytes() + } + + /// Returns the memory representation of this floating point number as a byte array in + /// native byte order. + /// + /// As the target platform's native endianness is used, portable code + /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead. + /// + /// [`to_be_bytes`]: f128::to_be_bytes + /// [`to_le_bytes`]: f128::to_le_bytes + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// + /// let bytes = 12.5f128.to_ne_bytes(); + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + /// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// } else { + /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40] + /// } + /// ); + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_ne_bytes(self) -> [u8; 16] { + self.to_bits().to_ne_bytes() + } + + /// Creates a floating point value from its representation as a byte array in big endian. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `eqtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let value = f128::from_be_bytes( + /// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// ); + /// assert_eq!(value, 12.5); + /// # } + /// ``` #[inline] #[must_use] #[unstable(feature = "f128", issue = "116909")] - pub fn from_bits(v: u128) -> Self { - // SAFETY: `u128 is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - unsafe { mem::transmute(v) } + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + pub const fn from_be_bytes(bytes: [u8; 16]) -> Self { + Self::from_bits(u128::from_be_bytes(bytes)) + } + + /// Creates a floating point value from its representation as a byte array in little endian. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `eqtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let value = f128::from_le_bytes( + /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40] + /// ); + /// assert_eq!(value, 12.5); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + pub const fn from_le_bytes(bytes: [u8; 16]) -> Self { + Self::from_bits(u128::from_le_bytes(bytes)) + } + + /// Creates a floating point value from its representation as a byte array in native endian. + /// + /// As the target platform's native endianness is used, portable code + /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as + /// appropriate instead. + /// + /// [`from_be_bytes`]: f128::from_be_bytes + /// [`from_le_bytes`]: f128::from_le_bytes + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `eqtf2` is available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let value = f128::from_ne_bytes(if cfg!(target_endian = "big") { + /// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// } else { + /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x40] + /// }); + /// assert_eq!(value, 12.5); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + pub const fn from_ne_bytes(bytes: [u8; 16]) -> Self { + Self::from_bits(u128::from_ne_bytes(bytes)) + } + + /// Returns the ordering between `self` and `other`. + /// + /// Unlike the standard partial comparison between floating point numbers, + /// this comparison always produces an ordering in accordance to + /// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision) + /// floating point standard. The values are ordered in the following sequence: + /// + /// - negative quiet NaN + /// - negative signaling NaN + /// - negative infinity + /// - negative numbers + /// - negative subnormal numbers + /// - negative zero + /// - positive zero + /// - positive subnormal numbers + /// - positive numbers + /// - positive infinity + /// - positive signaling NaN + /// - positive quiet NaN. + /// + /// The ordering established by this function does not always agree with the + /// [`PartialOrd`] and [`PartialEq`] implementations of `f128`. For example, + /// they consider negative and positive zero equal, while `total_cmp` + /// doesn't. + /// + /// The interpretation of the signaling NaN bit follows the definition in + /// the IEEE 754 standard, which may not match the interpretation by some of + /// the older, non-conformant (e.g. MIPS) hardware implementations. + /// + /// # Example + /// + /// ``` + /// #![feature(f128)] + /// + /// struct GoodBoy { + /// name: &'static str, + /// weight: f128, + /// } + /// + /// let mut bois = vec![ + /// GoodBoy { name: "Pucci", weight: 0.1 }, + /// GoodBoy { name: "Woofer", weight: 99.0 }, + /// GoodBoy { name: "Yapper", weight: 10.0 }, + /// GoodBoy { name: "Chonk", weight: f128::INFINITY }, + /// GoodBoy { name: "Abs. Unit", weight: f128::NAN }, + /// GoodBoy { name: "Floaty", weight: -5.0 }, + /// ]; + /// + /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight)); + /// + /// // `f128::NAN` could be positive or negative, which will affect the sort order. + /// if f128::NAN.is_sign_negative() { + /// bois.into_iter().map(|b| b.weight) + /// .zip([f128::NAN, -5.0, 0.1, 10.0, 99.0, f128::INFINITY].iter()) + /// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits())) + /// } else { + /// bois.into_iter().map(|b| b.weight) + /// .zip([-5.0, 0.1, 10.0, 99.0, f128::INFINITY, f128::NAN].iter()) + /// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits())) + /// } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f128", issue = "116909")] + pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { + let mut left = self.to_bits() as i128; + let mut right = other.to_bits() as i128; + + // In case of negatives, flip all the bits except the sign + // to achieve a similar layout as two's complement integers + // + // Why does this work? IEEE 754 floats consist of three fields: + // Sign bit, exponent and mantissa. The set of exponent and mantissa + // fields as a whole have the property that their bitwise order is + // equal to the numeric magnitude where the magnitude is defined. + // The magnitude is not normally defined on NaN values, but + // IEEE 754 totalOrder defines the NaN values also to follow the + // bitwise order. This leads to order explained in the doc comment. + // However, the representation of magnitude is the same for negative + // and positive numbers – only the sign bit is different. + // To easily compare the floats as signed integers, we need to + // flip the exponent and mantissa bits in case of negative numbers. + // We effectively convert the numbers to "two's complement" form. + // + // To do the flipping, we construct a mask and XOR against it. + // We branchlessly calculate an "all-ones except for the sign bit" + // mask from negative-signed values: right shifting sign-extends + // the integer, so we "fill" the mask with sign bits, and then + // convert to unsigned to push one more zero bit. + // On positive values, the mask is all zeros, so it's a no-op. + left ^= (((left >> 127) as u128) >> 1) as i128; + right ^= (((right >> 127) as u128) >> 1) as i128; + + left.cmp(&right) + } + + /// Restrict a value to a certain interval unless it is NaN. + /// + /// Returns `max` if `self` is greater than `max`, and `min` if `self` is + /// less than `min`. Otherwise this returns `self`. + /// + /// Note that this function returns NaN if the initial value was NaN as + /// well. + /// + /// # Panics + /// + /// Panics if `min > max`, `min` is NaN, or `max` is NaN. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # // FIXME(f16_f128): remove when `{eq,gt,unord}tf` are available + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// assert!((-3.0f128).clamp(-2.0, 1.0) == -2.0); + /// assert!((0.0f128).clamp(-2.0, 1.0) == 0.0); + /// assert!((2.0f128).clamp(-2.0, 1.0) == 1.0); + /// assert!((f128::NAN).clamp(-2.0, 1.0).is_nan()); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn clamp(mut self, min: f128, max: f128) -> f128 { + assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}"); + if self < min { + self = min; + } + if self > max { + self = max; + } + self } } diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index c4d4584544bad..054897b3c96bc 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -11,7 +11,11 @@ #![unstable(feature = "f16", issue = "116909")] +use crate::convert::FloatToInt; +#[cfg(not(test))] +use crate::intrinsics; use crate::mem; +use crate::num::FpCategory; /// Basic mathematical constants. #[unstable(feature = "f16", issue = "116909")] @@ -67,6 +71,12 @@ pub mod consts { // Also, #[unstable(feature = "more_float_constants", issue = "103883")] pub const FRAC_1_SQRT_PI: f16 = 0.564189583547756286948079451560772586_f16; + /// 1/sqrt(2π) + #[doc(alias = "FRAC_1_SQRT_TAU")] + #[unstable(feature = "f16", issue = "116909")] + // Also, #[unstable(feature = "more_float_constants", issue = "103883")] + pub const FRAC_1_SQRT_2PI: f16 = 0.398942280401432677939946059934381868_f16; + /// 2/π #[unstable(feature = "f16", issue = "116909")] pub const FRAC_2_PI: f16 = 0.636619772367581343075535053490057448_f16; @@ -209,7 +219,57 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] pub const MAX_10_EXP: i32 = 4; + /// Not a Number (NaN). + /// + /// Note that IEEE 754 doesn't define just a single NaN value; + /// a plethora of bit patterns are considered to be NaN. + /// Furthermore, the standard makes a difference + /// between a "signaling" and a "quiet" NaN, + /// and allows inspecting its "payload" (the unspecified bits in the bit pattern). + /// This constant isn't guaranteed to equal to any specific NaN bitpattern, + /// and the stability of its representation over Rust versions + /// and target platforms isn't guaranteed. + #[allow(clippy::eq_op)] + #[rustc_diagnostic_item = "f16_nan"] + #[unstable(feature = "f16", issue = "116909")] + pub const NAN: f16 = 0.0_f16 / 0.0_f16; + + /// Infinity (∞). + #[unstable(feature = "f16", issue = "116909")] + pub const INFINITY: f16 = 1.0_f16 / 0.0_f16; + + /// Negative infinity (−∞). + #[unstable(feature = "f16", issue = "116909")] + pub const NEG_INFINITY: f16 = -1.0_f16 / 0.0_f16; + + /// Sign bit + pub(crate) const SIGN_MASK: u16 = 0x8000; + + /// Exponent mask + pub(crate) const EXP_MASK: u16 = 0x7c00; + + /// Mantissa mask + pub(crate) const MAN_MASK: u16 = 0x03ff; + + /// Minimum representable positive value (min subnormal) + const TINY_BITS: u16 = 0x1; + + /// Minimum representable negative value (min negative subnormal) + const NEG_TINY_BITS: u16 = Self::TINY_BITS | Self::SIGN_MASK; + /// Returns `true` if this value is NaN. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let nan = f16::NAN; + /// let f = 7.0_f16; + /// + /// assert!(nan.is_nan()); + /// assert!(!f.is_nan()); + /// # } + /// ``` #[inline] #[must_use] #[unstable(feature = "f16", issue = "116909")] @@ -218,21 +278,238 @@ impl f16 { self != self } + // FIXMxE(#50145): `abs` is publicly unavailable in core due to + // concerns about portability, so this implementation is for + // private use internally. + #[inline] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub(crate) const fn abs_private(self) -> f16 { + // SAFETY: This transmutation is fine. Probably. For the reasons std is using it. + unsafe { mem::transmute::(mem::transmute::(self) & !Self::SIGN_MASK) } + } + + /// Returns `true` if this value is positive infinity or negative infinity, and + /// `false` otherwise. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let f = 7.0f16; + /// let inf = f16::INFINITY; + /// let neg_inf = f16::NEG_INFINITY; + /// let nan = f16::NAN; + /// + /// assert!(!f.is_infinite()); + /// assert!(!nan.is_infinite()); + /// + /// assert!(inf.is_infinite()); + /// assert!(neg_inf.is_infinite()); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn is_infinite(self) -> bool { + (self == f16::INFINITY) | (self == f16::NEG_INFINITY) + } + + /// Returns `true` if this number is neither infinite nor NaN. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let f = 7.0f16; + /// let inf: f16 = f16::INFINITY; + /// let neg_inf: f16 = f16::NEG_INFINITY; + /// let nan: f16 = f16::NAN; + /// + /// assert!(f.is_finite()); + /// + /// assert!(!nan.is_finite()); + /// assert!(!inf.is_finite()); + /// assert!(!neg_inf.is_finite()); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn is_finite(self) -> bool { + // There's no need to handle NaN separately: if self is NaN, + // the comparison is not true, exactly as desired. + self.abs_private() < Self::INFINITY + } + + /// Returns `true` if the number is [subnormal]. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let min = f16::MIN_POSITIVE; // 6.1035e-5 + /// let max = f16::MAX; + /// let lower_than_min = 1.0e-7_f16; + /// let zero = 0.0_f16; + /// + /// assert!(!min.is_subnormal()); + /// assert!(!max.is_subnormal()); + /// + /// assert!(!zero.is_subnormal()); + /// assert!(!f16::NAN.is_subnormal()); + /// assert!(!f16::INFINITY.is_subnormal()); + /// // Values between `0` and `min` are Subnormal. + /// assert!(lower_than_min.is_subnormal()); + /// # } + /// ``` + /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number + #[inline] + #[must_use] + #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn is_subnormal(self) -> bool { + matches!(self.classify(), FpCategory::Subnormal) + } + + /// Returns `true` if the number is neither zero, infinite, [subnormal], or NaN. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let min = f16::MIN_POSITIVE; // 6.1035e-5 + /// let max = f16::MAX; + /// let lower_than_min = 1.0e-7_f16; + /// let zero = 0.0_f16; + /// + /// assert!(min.is_normal()); + /// assert!(max.is_normal()); + /// + /// assert!(!zero.is_normal()); + /// assert!(!f16::NAN.is_normal()); + /// assert!(!f16::INFINITY.is_normal()); + /// // Values between `0` and `min` are Subnormal. + /// assert!(!lower_than_min.is_normal()); + /// # } + /// ``` + /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number + #[inline] + #[must_use] + #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn is_normal(self) -> bool { + matches!(self.classify(), FpCategory::Normal) + } + + /// Returns the floating point category of the number. If only one property + /// is going to be tested, it is generally faster to use the specific + /// predicate instead. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// use std::num::FpCategory; + /// + /// let num = 12.4_f16; + /// let inf = f16::INFINITY; + /// + /// assert_eq!(num.classify(), FpCategory::Normal); + /// assert_eq!(inf.classify(), FpCategory::Infinite); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + pub const fn classify(self) -> FpCategory { + // A previous implementation for f32/f64 tried to only use bitmask-based checks, + // using `to_bits` to transmute the float to its bit repr and match on that. + // Unfortunately, floating point numbers can be much worse than that. + // This also needs to not result in recursive evaluations of `to_bits`. + // + + // Platforms without native support generally convert to `f32` to perform operations, + // and most of these platforms correctly round back to `f16` after each operation. + // However, some platforms have bugs where they keep the excess `f32` precision (e.g. + // WASM, see llvm/llvm-project#96437). This implementation makes a best-effort attempt + // to account for that excess precision. + if self.is_infinite() { + // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask. + FpCategory::Infinite + } else if self.is_nan() { + // And it may not be NaN, as it can simply be an "overextended" finite value. + FpCategory::Nan + } else { + // However, std can't simply compare to zero to check for zero, either, + // as correctness requires avoiding equality tests that may be Subnormal == -0.0 + // because it may be wrong under "denormals are zero" and "flush to zero" modes. + // Most of std's targets don't use those, but they are used for thumbv7neon. + // So, this does use bitpattern matching for the rest. + + // SAFETY: f16 to u16 is fine. Usually. + // If classify has gotten this far, the value is definitely in one of these categories. + unsafe { f16::partial_classify(self) } + } + } + + /// This doesn't actually return a right answer for NaN on purpose, + /// seeing as how it cannot correctly discern between a floating point NaN, + /// and some normal floating point numbers truncated from an x87 FPU. + /// + /// # Safety + /// + /// This requires making sure you call this function for values it answers correctly on, + /// otherwise it returns a wrong answer. This is not important for memory safety per se, + /// but getting floats correct is important for not accidentally leaking const eval + /// runtime-deviating logic which may or may not be acceptable. + #[inline] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + const unsafe fn partial_classify(self) -> FpCategory { + // SAFETY: The caller is not asking questions for which this will tell lies. + let b = unsafe { mem::transmute::(self) }; + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { + (0, Self::EXP_MASK) => FpCategory::Infinite, + (0, 0) => FpCategory::Zero, + (_, 0) => FpCategory::Subnormal, + _ => FpCategory::Normal, + } + } + + /// This operates on bits, and only bits, so it can ignore concerns about weird FPUs. + /// FIXME(jubilee): In a just world, this would be the entire impl for classify, + /// plus a transmute. We do not live in a just world, but we can make it more so. + #[inline] + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] + const fn classify_bits(b: u16) -> FpCategory { + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { + (0, Self::EXP_MASK) => FpCategory::Infinite, + (_, Self::EXP_MASK) => FpCategory::Nan, + (0, 0) => FpCategory::Zero, + (_, 0) => FpCategory::Subnormal, + _ => FpCategory::Normal, + } + } + /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with /// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that /// the bit pattern of NaNs are conserved over arithmetic operations, the result of /// `is_sign_positive` on a NaN might produce an unexpected result in some cases. - /// See [explanation of NaN as a special value](f32) for more info. + /// See [explanation of NaN as a special value](f16) for more info. /// /// ``` /// #![feature(f16)] + /// # // FIXME(f16_f128): LLVM crashes on s390x, llvm/llvm-project#50374 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let f = 7.0_f16; /// let g = -7.0_f16; /// /// assert!(f.is_sign_positive()); /// assert!(!g.is_sign_positive()); + /// # } /// ``` #[inline] #[must_use] @@ -246,16 +523,19 @@ impl f16 { /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that /// the bit pattern of NaNs are conserved over arithmetic operations, the result of /// `is_sign_negative` on a NaN might produce an unexpected result in some cases. - /// See [explanation of NaN as a special value](f32) for more info. + /// See [explanation of NaN as a special value](f16) for more info. /// /// ``` /// #![feature(f16)] + /// # // FIXME(f16_f128): LLVM crashes on s390x, llvm/llvm-project#50374 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let f = 7.0_f16; /// let g = -7.0_f16; /// /// assert!(!f.is_sign_negative()); /// assert!(g.is_sign_negative()); + /// # } /// ``` #[inline] #[must_use] @@ -267,6 +547,215 @@ impl f16 { (self.to_bits() & (1 << 15)) != 0 } + /// Returns the least number greater than `self`. + /// + /// Let `TINY` be the smallest representable positive `f16`. Then, + /// - if `self.is_nan()`, this returns `self`; + /// - if `self` is [`NEG_INFINITY`], this returns [`MIN`]; + /// - if `self` is `-TINY`, this returns -0.0; + /// - if `self` is -0.0 or +0.0, this returns `TINY`; + /// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`]; + /// - otherwise the unique least value greater than `self` is returned. + /// + /// The identity `x.next_up() == -(-x).next_down()` holds for all non-NaN `x`. When `x` + /// is finite `x == x.next_up().next_down()` also holds. + /// + /// ```rust + /// #![feature(f16)] + /// #![feature(float_next_up_down)] + /// # // FIXME(f16_f128): ABI issues on MSVC + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// // f16::EPSILON is the difference between 1.0 and the next number up. + /// assert_eq!(1.0f16.next_up(), 1.0 + f16::EPSILON); + /// // But not for most numbers. + /// assert!(0.1f16.next_up() < 0.1 + f16::EPSILON); + /// assert_eq!(4356f16.next_up(), 4360.0); + /// # } + /// ``` + /// + /// [`NEG_INFINITY`]: Self::NEG_INFINITY + /// [`INFINITY`]: Self::INFINITY + /// [`MIN`]: Self::MIN + /// [`MAX`]: Self::MAX + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_next_up_down", issue = "91399")] + pub fn next_up(self) -> Self { + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. + let bits = self.to_bits(); + if self.is_nan() || bits == Self::INFINITY.to_bits() { + return self; + } + + let abs = bits & !Self::SIGN_MASK; + let next_bits = if abs == 0 { + Self::TINY_BITS + } else if bits == abs { + bits + 1 + } else { + bits - 1 + }; + Self::from_bits(next_bits) + } + + /// Returns the greatest number less than `self`. + /// + /// Let `TINY` be the smallest representable positive `f16`. Then, + /// - if `self.is_nan()`, this returns `self`; + /// - if `self` is [`INFINITY`], this returns [`MAX`]; + /// - if `self` is `TINY`, this returns 0.0; + /// - if `self` is -0.0 or +0.0, this returns `-TINY`; + /// - if `self` is [`MIN`] or [`NEG_INFINITY`], this returns [`NEG_INFINITY`]; + /// - otherwise the unique greatest value less than `self` is returned. + /// + /// The identity `x.next_down() == -(-x).next_up()` holds for all non-NaN `x`. When `x` + /// is finite `x == x.next_down().next_up()` also holds. + /// + /// ```rust + /// #![feature(f16)] + /// #![feature(float_next_up_down)] + /// # // FIXME(f16_f128): ABI issues on MSVC + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let x = 1.0f16; + /// // Clamp value into range [0, 1). + /// let clamped = x.clamp(0.0, 1.0f16.next_down()); + /// assert!(clamped < 1.0); + /// assert_eq!(clamped.next_up(), 1.0); + /// # } + /// ``` + /// + /// [`NEG_INFINITY`]: Self::NEG_INFINITY + /// [`INFINITY`]: Self::INFINITY + /// [`MIN`]: Self::MIN + /// [`MAX`]: Self::MAX + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_next_up_down", issue = "91399")] + pub fn next_down(self) -> Self { + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. + let bits = self.to_bits(); + if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() { + return self; + } + + let abs = bits & !Self::SIGN_MASK; + let next_bits = if abs == 0 { + Self::NEG_TINY_BITS + } else if bits == abs { + bits - 1 + } else { + bits + 1 + }; + Self::from_bits(next_bits) + } + + /// Takes the reciprocal (inverse) of a number, `1/x`. + /// + /// ``` + /// #![feature(f16)] + /// # // FIXME(f16_f128): extendhfsf2, truncsfhf2, __gnu_h2f_ieee, __gnu_f2h_ieee missing for many platforms + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.recip() - (1.0 / x)).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn recip(self) -> Self { + 1.0 / self + } + + /// Converts radians to degrees. + /// + /// ``` + /// #![feature(f16)] + /// # // FIXME(f16_f128): extendhfsf2, truncsfhf2, __gnu_h2f_ieee, __gnu_f2h_ieee missing for many platforms + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let angle = std::f16::consts::PI; + /// + /// let abs_difference = (angle.to_degrees() - 180.0).abs(); + /// assert!(abs_difference <= 0.5); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_degrees(self) -> Self { + // Use a literal for better precision. + const PIS_IN_180: f16 = 57.2957795130823208767981548141051703_f16; + self * PIS_IN_180 + } + + /// Converts degrees to radians. + /// + /// ``` + /// #![feature(f16)] + /// # // FIXME(f16_f128): extendhfsf2, truncsfhf2, __gnu_h2f_ieee, __gnu_f2h_ieee missing for many platforms + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let angle = 180.0f16; + /// + /// let abs_difference = (angle.to_radians() - std::f16::consts::PI).abs(); + /// + /// assert!(abs_difference <= 0.01); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn to_radians(self) -> f16 { + // Use a literal for better precision. + const RADS_PER_DEG: f16 = 0.017453292519943295769236907684886_f16; + self * RADS_PER_DEG + } + + /// Rounds toward zero and converts to any primitive integer type, + /// assuming that the value is finite and fits in that type. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let value = 4.6_f16; + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, 4); + /// + /// let value = -128.9_f16; + /// let rounded = unsafe { value.to_int_unchecked::() }; + /// assert_eq!(rounded, i8::MIN); + /// # } + /// ``` + /// + /// # Safety + /// + /// The value must: + /// + /// * Not be `NaN` + /// * Not be infinite + /// * Be representable in the return type `Int`, after truncating off its fractional part + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub unsafe fn to_int_unchecked(self) -> Int + where + Self: FloatToInt, + { + // SAFETY: the caller must uphold the safety contract for + // `FloatToInt::to_int_unchecked`. + unsafe { FloatToInt::::to_int_unchecked(self) } + } + /// Raw transmutation to `u16`. /// /// This is currently identical to `transmute::(self)` on all platforms. @@ -276,14 +765,64 @@ impl f16 { /// /// Note that this function is distinct from `as` casting, which attempts to /// preserve the *numeric* value, and not the bitwise value. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// # // FIXME(f16_f128): enable this once const casting works + /// # // assert_ne!((1f16).to_bits(), 1f16 as u128); // to_bits() is not casting! + /// assert_eq!((12.5f16).to_bits(), 0x4a40); + /// # } + /// ``` #[inline] #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] #[must_use = "this returns the result of the operation, without modifying the original"] - pub fn to_bits(self) -> u16 { - // SAFETY: `u16` is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - unsafe { mem::transmute(self) } + pub const fn to_bits(self) -> u16 { + // SAFETY: `u16` is a plain old datatype so we can always transmute to it. + // ...sorta. + // + // It turns out that at runtime, it is possible for a floating point number + // to be subject to a floating point mode that alters nonzero subnormal numbers + // to zero on reads and writes, aka "denormals are zero" and "flush to zero". + // + // And, of course evaluating to a NaN value is fairly nondeterministic. + // More precisely: when NaN should be returned is knowable, but which NaN? + // So far that's defined by a combination of LLVM and the CPU, not Rust. + // This function, however, allows observing the bitstring of a NaN, + // thus introspection on CTFE. + // + // In order to preserve, at least for the moment, const-to-runtime equivalence, + // we reject any of these possible situations from happening. + #[inline] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + const fn ct_f16_to_u16(ct: f16) -> u16 { + // FIXME(f16_f128): we should use `.classify()` like `f32` and `f64`, but we don't yet + // want to rely on that on all platforms because it is nondeterministic (e.g. x86 has + // convention discrepancies calling intrinsics). So just classify the bits instead. + + // SAFETY: this is a POD transmutation + let bits = unsafe { mem::transmute::(ct) }; + match f16::classify_bits(bits) { + FpCategory::Nan => { + panic!("const-eval error: cannot use f16::to_bits on a NaN") + } + FpCategory::Subnormal => { + panic!("const-eval error: cannot use f16::to_bits on a subnormal number") + } + FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => bits, + } + } + + #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491 + fn rt_f16_to_u16(x: f16) -> u16 { + // SAFETY: `u16` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute(x) } + } + intrinsics::const_eval_select((self,), ct_f16_to_u16, rt_f16_to_u16) } /// Raw transmutation from `u16`. @@ -315,13 +854,368 @@ impl f16 { /// /// Note that this function is distinct from `as` casting, which attempts to /// preserve the *numeric* value, and not the bitwise value. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let v = f16::from_bits(0x4a40); + /// assert_eq!(v, 12.5); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + pub const fn from_bits(v: u16) -> Self { + // It turns out the safety issues with sNaN were overblown! Hooray! + // SAFETY: `u16` is a plain old datatype so we can always transmute from it + // ...sorta. + // + // It turns out that at runtime, it is possible for a floating point number + // to be subject to floating point modes that alter nonzero subnormal numbers + // to zero on reads and writes, aka "denormals are zero" and "flush to zero". + // This is not a problem usually, but at least one tier2 platform for Rust + // actually exhibits this behavior by default: thumbv7neon + // aka "the Neon FPU in AArch32 state" + // + // And, of course evaluating to a NaN value is fairly nondeterministic. + // More precisely: when NaN should be returned is knowable, but which NaN? + // So far that's defined by a combination of LLVM and the CPU, not Rust. + // This function, however, allows observing the bitstring of a NaN, + // thus introspection on CTFE. + // + // In order to preserve, at least for the moment, const-to-runtime equivalence, + // reject any of these possible situations from happening. + #[inline] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + const fn ct_u16_to_f16(ct: u16) -> f16 { + match f16::classify_bits(ct) { + FpCategory::Subnormal => { + panic!("const-eval error: cannot use f16::from_bits on a subnormal number") + } + FpCategory::Nan => { + panic!("const-eval error: cannot use f16::from_bits on NaN") + } + FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => { + // SAFETY: It's not a frumious number + unsafe { mem::transmute::(ct) } + } + } + } + + #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491 + fn rt_u16_to_f16(x: u16) -> f16 { + // SAFETY: `u16` is a plain old datatype so we can always... uh... + // ...look, just pretend you forgot what you just read. + // Stability concerns. + unsafe { mem::transmute(x) } + } + intrinsics::const_eval_select((v,), ct_u16_to_f16, rt_u16_to_f16) + } + + /// Returns the memory representation of this floating point number as a byte array in + /// big-endian (network) byte order. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # // FIXME(f16_f128): LLVM crashes on s390x, llvm/llvm-project#50374 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let bytes = 12.5f16.to_be_bytes(); + /// assert_eq!(bytes, [0x4a, 0x40]); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 2] { + self.to_bits().to_be_bytes() + } + + /// Returns the memory representation of this floating point number as a byte array in + /// little-endian byte order. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # // FIXME(f16_f128): LLVM crashes on s390x, llvm/llvm-project#50374 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let bytes = 12.5f16.to_le_bytes(); + /// assert_eq!(bytes, [0x40, 0x4a]); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 2] { + self.to_bits().to_le_bytes() + } + + /// Returns the memory representation of this floating point number as a byte array in + /// native byte order. + /// + /// As the target platform's native endianness is used, portable code + /// should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead. + /// + /// [`to_be_bytes`]: f16::to_be_bytes + /// [`to_le_bytes`]: f16::to_le_bytes + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # // FIXME(f16_f128): LLVM crashes on s390x, llvm/llvm-project#50374 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// let bytes = 12.5f16.to_ne_bytes(); + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + /// [0x4a, 0x40] + /// } else { + /// [0x40, 0x4a] + /// } + /// ); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_ne_bytes(self) -> [u8; 2] { + self.to_bits().to_ne_bytes() + } + + /// Creates a floating point value from its representation as a byte array in big endian. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let value = f16::from_be_bytes([0x4a, 0x40]); + /// assert_eq!(value, 12.5); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + pub const fn from_be_bytes(bytes: [u8; 2]) -> Self { + Self::from_bits(u16::from_be_bytes(bytes)) + } + + /// Creates a floating point value from its representation as a byte array in little endian. + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let value = f16::from_le_bytes([0x40, 0x4a]); + /// assert_eq!(value, 12.5); + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f16", issue = "116909")] + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + pub const fn from_le_bytes(bytes: [u8; 2]) -> Self { + Self::from_bits(u16::from_le_bytes(bytes)) + } + + /// Creates a floating point value from its representation as a byte array in native endian. + /// + /// As the target platform's native endianness is used, portable code + /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as + /// appropriate instead. + /// + /// [`from_be_bytes`]: f16::from_be_bytes + /// [`from_le_bytes`]: f16::from_le_bytes + /// + /// See [`from_bits`](Self::from_bits) for some discussion of the + /// portability of this operation (there are almost no issues). + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let value = f16::from_ne_bytes(if cfg!(target_endian = "big") { + /// [0x4a, 0x40] + /// } else { + /// [0x40, 0x4a] + /// }); + /// assert_eq!(value, 12.5); + /// # } + /// ``` #[inline] #[must_use] #[unstable(feature = "f16", issue = "116909")] - pub fn from_bits(v: u16) -> Self { - // SAFETY: `u16` is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - unsafe { mem::transmute(v) } + #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] + pub const fn from_ne_bytes(bytes: [u8; 2]) -> Self { + Self::from_bits(u16::from_ne_bytes(bytes)) + } + + /// Returns the ordering between `self` and `other`. + /// + /// Unlike the standard partial comparison between floating point numbers, + /// this comparison always produces an ordering in accordance to + /// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision) + /// floating point standard. The values are ordered in the following sequence: + /// + /// - negative quiet NaN + /// - negative signaling NaN + /// - negative infinity + /// - negative numbers + /// - negative subnormal numbers + /// - negative zero + /// - positive zero + /// - positive subnormal numbers + /// - positive numbers + /// - positive infinity + /// - positive signaling NaN + /// - positive quiet NaN. + /// + /// The ordering established by this function does not always agree with the + /// [`PartialOrd`] and [`PartialEq`] implementations of `f16`. For example, + /// they consider negative and positive zero equal, while `total_cmp` + /// doesn't. + /// + /// The interpretation of the signaling NaN bit follows the definition in + /// the IEEE 754 standard, which may not match the interpretation by some of + /// the older, non-conformant (e.g. MIPS) hardware implementations. + /// + /// # Example + /// + /// ``` + /// #![feature(f16)] + /// # // FIXME(f16_f128): extendhfsf2, truncsfhf2, __gnu_h2f_ieee, __gnu_f2h_ieee missing for many platforms + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// + /// struct GoodBoy { + /// name: &'static str, + /// weight: f16, + /// } + /// + /// let mut bois = vec![ + /// GoodBoy { name: "Pucci", weight: 0.1 }, + /// GoodBoy { name: "Woofer", weight: 99.0 }, + /// GoodBoy { name: "Yapper", weight: 10.0 }, + /// GoodBoy { name: "Chonk", weight: f16::INFINITY }, + /// GoodBoy { name: "Abs. Unit", weight: f16::NAN }, + /// GoodBoy { name: "Floaty", weight: -5.0 }, + /// ]; + /// + /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight)); + /// + /// // `f16::NAN` could be positive or negative, which will affect the sort order. + /// if f16::NAN.is_sign_negative() { + /// bois.into_iter().map(|b| b.weight) + /// .zip([f16::NAN, -5.0, 0.1, 10.0, 99.0, f16::INFINITY].iter()) + /// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits())) + /// } else { + /// bois.into_iter().map(|b| b.weight) + /// .zip([-5.0, 0.1, 10.0, 99.0, f16::INFINITY, f16::NAN].iter()) + /// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits())) + /// } + /// # } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "f16", issue = "116909")] + pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { + let mut left = self.to_bits() as i16; + let mut right = other.to_bits() as i16; + + // In case of negatives, flip all the bits except the sign + // to achieve a similar layout as two's complement integers + // + // Why does this work? IEEE 754 floats consist of three fields: + // Sign bit, exponent and mantissa. The set of exponent and mantissa + // fields as a whole have the property that their bitwise order is + // equal to the numeric magnitude where the magnitude is defined. + // The magnitude is not normally defined on NaN values, but + // IEEE 754 totalOrder defines the NaN values also to follow the + // bitwise order. This leads to order explained in the doc comment. + // However, the representation of magnitude is the same for negative + // and positive numbers – only the sign bit is different. + // To easily compare the floats as signed integers, we need to + // flip the exponent and mantissa bits in case of negative numbers. + // We effectively convert the numbers to "two's complement" form. + // + // To do the flipping, we construct a mask and XOR against it. + // We branchlessly calculate an "all-ones except for the sign bit" + // mask from negative-signed values: right shifting sign-extends + // the integer, so we "fill" the mask with sign bits, and then + // convert to unsigned to push one more zero bit. + // On positive values, the mask is all zeros, so it's a no-op. + left ^= (((left >> 15) as u16) >> 1) as i16; + right ^= (((right >> 15) as u16) >> 1) as i16; + + left.cmp(&right) + } + + /// Restrict a value to a certain interval unless it is NaN. + /// + /// Returns `max` if `self` is greater than `max`, and `min` if `self` is + /// less than `min`. Otherwise this returns `self`. + /// + /// Note that this function returns NaN if the initial value was NaN as + /// well. + /// + /// # Panics + /// + /// Panics if `min > max`, `min` is NaN, or `max` is NaN. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// assert!((-3.0f16).clamp(-2.0, 1.0) == -2.0); + /// assert!((0.0f16).clamp(-2.0, 1.0) == 0.0); + /// assert!((2.0f16).clamp(-2.0, 1.0) == 1.0); + /// assert!((f16::NAN).clamp(-2.0, 1.0).is_nan()); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn clamp(mut self, min: f16, max: f16) -> f16 { + assert!(min <= max, "min > max, or either was NaN. min = {min:?}, max = {max:?}"); + if self < min { + self = min; + } + if self > max { + self = max; + } + self } } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 2e715fb0bdde7..08d863f17caf7 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -327,6 +327,11 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "103883")] pub const FRAC_1_SQRT_PI: f32 = 0.564189583547756286948079451560772586_f32; + /// 1/sqrt(2π) + #[doc(alias = "FRAC_1_SQRT_TAU")] + #[unstable(feature = "more_float_constants", issue = "103883")] + pub const FRAC_1_SQRT_2PI: f32 = 0.398942280401432677939946059934381868_f32; + /// 2/π #[stable(feature = "rust1", since = "1.0.0")] pub const FRAC_2_PI: f32 = 0.636619772367581343075535053490057448_f32; @@ -485,6 +490,21 @@ impl f32 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32; + /// Sign bit + const SIGN_MASK: u32 = 0x8000_0000; + + /// Exponent mask + const EXP_MASK: u32 = 0x7f80_0000; + + /// Mantissa mask + const MAN_MASK: u32 = 0x007f_ffff; + + /// Minimum representable positive value (min subnormal) + const TINY_BITS: u32 = 0x1; + + /// Minimum representable negative value (min negative subnormal) + const NEG_TINY_BITS: u32 = Self::TINY_BITS | Self::SIGN_MASK; + /// Returns `true` if this value is NaN. /// /// ``` @@ -510,7 +530,7 @@ impl f32 { #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub(crate) const fn abs_private(self) -> f32 { // SAFETY: This transmutation is fine. Probably. For the reasons std is using it. - unsafe { mem::transmute::(mem::transmute::(self) & 0x7fff_ffff) } + unsafe { mem::transmute::(mem::transmute::(self) & !Self::SIGN_MASK) } } /// Returns `true` if this value is positive infinity or negative infinity, and @@ -677,12 +697,9 @@ impl f32 { // runtime-deviating logic which may or may not be acceptable. #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] const unsafe fn partial_classify(self) -> FpCategory { - const EXP_MASK: u32 = 0x7f800000; - const MAN_MASK: u32 = 0x007fffff; - // SAFETY: The caller is not asking questions for which this will tell lies. let b = unsafe { mem::transmute::(self) }; - match (b & MAN_MASK, b & EXP_MASK) { + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, _ => FpCategory::Normal, @@ -694,12 +711,9 @@ impl f32 { // plus a transmute. We do not live in a just world, but we can make it more so. #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] const fn classify_bits(b: u32) -> FpCategory { - const EXP_MASK: u32 = 0x7f800000; - const MAN_MASK: u32 = 0x007fffff; - - match (b & MAN_MASK, b & EXP_MASK) { - (0, EXP_MASK) => FpCategory::Infinite, - (_, EXP_MASK) => FpCategory::Nan, + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { + (0, Self::EXP_MASK) => FpCategory::Infinite, + (_, Self::EXP_MASK) => FpCategory::Nan, (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, _ => FpCategory::Normal, @@ -707,11 +721,13 @@ impl f32 { } /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with - /// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any - /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that - /// the bit pattern of NaNs are conserved over arithmetic operations, the result of - /// `is_sign_positive` on a NaN might produce an unexpected result in some cases. - /// See [explanation of NaN as a special value](f32) for more info. + /// positive sign bit and positive infinity. + /// + /// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of + /// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are + /// conserved over arithmetic operations, the result of `is_sign_positive` on + /// a NaN might produce an unexpected result in some cases. See [explanation + /// of NaN as a special value](f32) for more info. /// /// ``` /// let f = 7.0_f32; @@ -729,11 +745,13 @@ impl f32 { } /// Returns `true` if `self` has a negative sign, including `-0.0`, NaNs with - /// negative sign bit and negative infinity. Note that IEEE 754 doesn't assign any - /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that - /// the bit pattern of NaNs are conserved over arithmetic operations, the result of - /// `is_sign_negative` on a NaN might produce an unexpected result in some cases. - /// See [explanation of NaN as a special value](f32) for more info. + /// negative sign bit and negative infinity. + /// + /// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of + /// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are + /// conserved over arithmetic operations, the result of `is_sign_negative` on + /// a NaN might produce an unexpected result in some cases. See [explanation + /// of NaN as a special value](f32) for more info. /// /// ``` /// let f = 7.0f32; @@ -782,19 +800,17 @@ impl f32 { #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { - // We must use strictly integer arithmetic to prevent denormals from - // flushing to zero after an arithmetic operation on some platforms. - const TINY_BITS: u32 = 0x1; // Smallest positive f32. - const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff; - + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. let bits = self.to_bits(); if self.is_nan() || bits == Self::INFINITY.to_bits() { return self; } - let abs = bits & CLEAR_SIGN_MASK; + let abs = bits & !Self::SIGN_MASK; let next_bits = if abs == 0 { - TINY_BITS + Self::TINY_BITS } else if bits == abs { bits + 1 } else { @@ -832,19 +848,17 @@ impl f32 { #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { - // We must use strictly integer arithmetic to prevent denormals from - // flushing to zero after an arithmetic operation on some platforms. - const NEG_TINY_BITS: u32 = 0x8000_0001; // Smallest (in magnitude) negative f32. - const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff; - + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. let bits = self.to_bits(); if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() { return self; } - let abs = bits & CLEAR_SIGN_MASK; + let abs = bits & !Self::SIGN_MASK; let next_bits = if abs == 0 { - NEG_TINY_BITS + Self::NEG_TINY_BITS } else if bits == abs { bits - 1 } else { @@ -901,8 +915,8 @@ impl f32 { #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] #[inline] pub fn to_radians(self) -> f32 { - let value: f32 = consts::PI; - self * (value / 180.0f32) + const RADS_PER_DEG: f32 = consts::PI / 180.0; + self * RADS_PER_DEG } /// Returns the maximum of the two numbers, ignoring NaN. @@ -1030,25 +1044,42 @@ impl f32 { /// ``` #[unstable(feature = "num_midpoint", issue = "110840")] pub fn midpoint(self, other: f32) -> f32 { - const LO: f32 = f32::MIN_POSITIVE * 2.; - const HI: f32 = f32::MAX / 2.; - - let (a, b) = (self, other); - let abs_a = a.abs_private(); - let abs_b = b.abs_private(); - - if abs_a <= HI && abs_b <= HI { - // Overflow is impossible - (a + b) / 2. - } else if abs_a < LO { - // Not safe to halve a - a + (b / 2.) - } else if abs_b < LO { - // Not safe to halve b - (a / 2.) + b - } else { - // Not safe to halve a and b - (a / 2.) + (b / 2.) + cfg_if! { + if #[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + all(any(target_arch="riscv32", target_arch= "riscv64"), target_feature="d"), + all(target_arch = "arm", target_feature="vfp2"), + target_arch = "wasm32", + target_arch = "wasm64", + ))] { + // whitelist the faster implementation to targets that have known good 64-bit float + // implementations. Falling back to the branchy code on targets that don't have + // 64-bit hardware floats or buggy implementations. + // see: https://github.com/rust-lang/rust/pull/121062#issuecomment-2123408114 + ((f64::from(self) + f64::from(other)) / 2.0) as f32 + } else { + const LO: f32 = f32::MIN_POSITIVE * 2.; + const HI: f32 = f32::MAX / 2.; + + let (a, b) = (self, other); + let abs_a = a.abs_private(); + let abs_b = b.abs_private(); + + if abs_a <= HI && abs_b <= HI { + // Overflow is impossible + (a + b) / 2. + } else if abs_a < LO { + // Not safe to halve a + a + (b / 2.) + } else if abs_b < LO { + // Not safe to halve b + (a / 2.) + b + } else { + // Not safe to halve a and b + (a / 2.) + (b / 2.) + } + } } } @@ -1247,7 +1278,7 @@ impl f32 { intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32) } - /// Return the memory representation of this floating point number as a byte array in + /// Returns the memory representation of this floating point number as a byte array in /// big-endian (network) byte order. /// /// See [`from_bits`](Self::from_bits) for some discussion of the @@ -1268,7 +1299,7 @@ impl f32 { self.to_bits().to_be_bytes() } - /// Return the memory representation of this floating point number as a byte array in + /// Returns the memory representation of this floating point number as a byte array in /// little-endian byte order. /// /// See [`from_bits`](Self::from_bits) for some discussion of the @@ -1289,7 +1320,7 @@ impl f32 { self.to_bits().to_le_bytes() } - /// Return the memory representation of this floating point number as a byte array in + /// Returns the memory representation of this floating point number as a byte array in /// native byte order. /// /// As the target platform's native endianness is used, portable code @@ -1323,7 +1354,7 @@ impl f32 { self.to_bits().to_ne_bytes() } - /// Create a floating point value from its representation as a byte array in big endian. + /// Creates a floating point value from its representation as a byte array in big endian. /// /// See [`from_bits`](Self::from_bits) for some discussion of the /// portability of this operation (there are almost no issues). @@ -1342,7 +1373,7 @@ impl f32 { Self::from_bits(u32::from_be_bytes(bytes)) } - /// Create a floating point value from its representation as a byte array in little endian. + /// Creates a floating point value from its representation as a byte array in little endian. /// /// See [`from_bits`](Self::from_bits) for some discussion of the /// portability of this operation (there are almost no issues). @@ -1361,7 +1392,7 @@ impl f32 { Self::from_bits(u32::from_le_bytes(bytes)) } - /// Create a floating point value from its representation as a byte array in native endian. + /// Creates a floating point value from its representation as a byte array in native endian. /// /// As the target platform's native endianness is used, portable code /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as @@ -1391,7 +1422,7 @@ impl f32 { Self::from_bits(u32::from_ne_bytes(bytes)) } - /// Return the ordering between `self` and `other`. + /// Returns the ordering between `self` and `other`. /// /// Unlike the standard partial comparison between floating point numbers, /// this comparison always produces an ordering in accordance to diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index db8e1f318adba..5d33eea6d011f 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -327,6 +327,11 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "103883")] pub const FRAC_1_SQRT_PI: f64 = 0.564189583547756286948079451560772586_f64; + /// 1/sqrt(2π) + #[doc(alias = "FRAC_1_SQRT_TAU")] + #[unstable(feature = "more_float_constants", issue = "103883")] + pub const FRAC_1_SQRT_2PI: f64 = 0.398942280401432677939946059934381868_f64; + /// 2/π #[stable(feature = "rust1", since = "1.0.0")] pub const FRAC_2_PI: f64 = 0.636619772367581343075535053490057448_f64; @@ -484,6 +489,21 @@ impl f64 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64; + /// Sign bit + const SIGN_MASK: u64 = 0x8000_0000_0000_0000; + + /// Exponent mask + const EXP_MASK: u64 = 0x7ff0_0000_0000_0000; + + /// Mantissa mask + const MAN_MASK: u64 = 0x000f_ffff_ffff_ffff; + + /// Minimum representable positive value (min subnormal) + const TINY_BITS: u64 = 0x1; + + /// Minimum representable negative value (min negative subnormal) + const NEG_TINY_BITS: u64 = Self::TINY_BITS | Self::SIGN_MASK; + /// Returns `true` if this value is NaN. /// /// ``` @@ -509,9 +529,7 @@ impl f64 { #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub(crate) const fn abs_private(self) -> f64 { // SAFETY: This transmutation is fine. Probably. For the reasons std is using it. - unsafe { - mem::transmute::(mem::transmute::(self) & 0x7fff_ffff_ffff_ffff) - } + unsafe { mem::transmute::(mem::transmute::(self) & !Self::SIGN_MASK) } } /// Returns `true` if this value is positive infinity or negative infinity, and @@ -668,13 +686,10 @@ impl f64 { // and some normal floating point numbers truncated from an x87 FPU. #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] const unsafe fn partial_classify(self) -> FpCategory { - const EXP_MASK: u64 = 0x7ff0000000000000; - const MAN_MASK: u64 = 0x000fffffffffffff; - // SAFETY: The caller is not asking questions for which this will tell lies. let b = unsafe { mem::transmute::(self) }; - match (b & MAN_MASK, b & EXP_MASK) { - (0, EXP_MASK) => FpCategory::Infinite, + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { + (0, Self::EXP_MASK) => FpCategory::Infinite, (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, _ => FpCategory::Normal, @@ -686,12 +701,9 @@ impl f64 { // plus a transmute. We do not live in a just world, but we can make it more so. #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] const fn classify_bits(b: u64) -> FpCategory { - const EXP_MASK: u64 = 0x7ff0000000000000; - const MAN_MASK: u64 = 0x000fffffffffffff; - - match (b & MAN_MASK, b & EXP_MASK) { - (0, EXP_MASK) => FpCategory::Infinite, - (_, EXP_MASK) => FpCategory::Nan, + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { + (0, Self::EXP_MASK) => FpCategory::Infinite, + (_, Self::EXP_MASK) => FpCategory::Nan, (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, _ => FpCategory::Normal, @@ -699,11 +711,13 @@ impl f64 { } /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with - /// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any - /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that - /// the bit pattern of NaNs are conserved over arithmetic operations, the result of - /// `is_sign_positive` on a NaN might produce an unexpected result in some cases. - /// See [explanation of NaN as a special value](f32) for more info. + /// positive sign bit and positive infinity. + /// + /// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of + /// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are + /// conserved over arithmetic operations, the result of `is_sign_positive` on + /// a NaN might produce an unexpected result in some cases. See [explanation + /// of NaN as a special value](f32) for more info. /// /// ``` /// let f = 7.0_f64; @@ -730,11 +744,13 @@ impl f64 { } /// Returns `true` if `self` has a negative sign, including `-0.0`, NaNs with - /// negative sign bit and negative infinity. Note that IEEE 754 doesn't assign any - /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that - /// the bit pattern of NaNs are conserved over arithmetic operations, the result of - /// `is_sign_negative` on a NaN might produce an unexpected result in some cases. - /// See [explanation of NaN as a special value](f32) for more info. + /// negative sign bit and negative infinity. + /// + /// Note that IEEE 754 doesn't assign any meaning to the sign bit in case of + /// a NaN, and as Rust doesn't guarantee that the bit pattern of NaNs are + /// conserved over arithmetic operations, the result of `is_sign_negative` on + /// a NaN might produce an unexpected result in some cases. See [explanation + /// of NaN as a special value](f32) for more info. /// /// ``` /// let f = 7.0_f64; @@ -751,7 +767,7 @@ impl f64 { // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus // applies to zeros and NaNs as well. // SAFETY: This is just transmuting to get the sign bit, it's fine. - unsafe { mem::transmute::(self) & 0x8000_0000_0000_0000 != 0 } + unsafe { mem::transmute::(self) & Self::SIGN_MASK != 0 } } #[must_use] @@ -792,19 +808,17 @@ impl f64 { #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { - // We must use strictly integer arithmetic to prevent denormals from - // flushing to zero after an arithmetic operation on some platforms. - const TINY_BITS: u64 = 0x1; // Smallest positive f64. - const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff; - + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. let bits = self.to_bits(); if self.is_nan() || bits == Self::INFINITY.to_bits() { return self; } - let abs = bits & CLEAR_SIGN_MASK; + let abs = bits & !Self::SIGN_MASK; let next_bits = if abs == 0 { - TINY_BITS + Self::TINY_BITS } else if bits == abs { bits + 1 } else { @@ -842,19 +856,17 @@ impl f64 { #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { - // We must use strictly integer arithmetic to prevent denormals from - // flushing to zero after an arithmetic operation on some platforms. - const NEG_TINY_BITS: u64 = 0x8000_0000_0000_0001; // Smallest (in magnitude) negative f64. - const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff; - + // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing + // denormals to zero. This is in general unsound and unsupported, but here + // we do our best to still produce the correct result on such targets. let bits = self.to_bits(); if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() { return self; } - let abs = bits & CLEAR_SIGN_MASK; + let abs = bits & !Self::SIGN_MASK; let next_bits = if abs == 0 { - NEG_TINY_BITS + Self::NEG_TINY_BITS } else if bits == abs { bits - 1 } else { @@ -912,8 +924,8 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn to_radians(self) -> f64 { - let value: f64 = consts::PI; - self * (value / 180.0) + const RADS_PER_DEG: f64 = consts::PI / 180.0; + self * RADS_PER_DEG } /// Returns the maximum of the two numbers, ignoring NaN. @@ -1244,7 +1256,7 @@ impl f64 { intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64) } - /// Return the memory representation of this floating point number as a byte array in + /// Returns the memory representation of this floating point number as a byte array in /// big-endian (network) byte order. /// /// See [`from_bits`](Self::from_bits) for some discussion of the @@ -1265,7 +1277,7 @@ impl f64 { self.to_bits().to_be_bytes() } - /// Return the memory representation of this floating point number as a byte array in + /// Returns the memory representation of this floating point number as a byte array in /// little-endian byte order. /// /// See [`from_bits`](Self::from_bits) for some discussion of the @@ -1286,7 +1298,7 @@ impl f64 { self.to_bits().to_le_bytes() } - /// Return the memory representation of this floating point number as a byte array in + /// Returns the memory representation of this floating point number as a byte array in /// native byte order. /// /// As the target platform's native endianness is used, portable code @@ -1320,7 +1332,7 @@ impl f64 { self.to_bits().to_ne_bytes() } - /// Create a floating point value from its representation as a byte array in big endian. + /// Creates a floating point value from its representation as a byte array in big endian. /// /// See [`from_bits`](Self::from_bits) for some discussion of the /// portability of this operation (there are almost no issues). @@ -1339,7 +1351,7 @@ impl f64 { Self::from_bits(u64::from_be_bytes(bytes)) } - /// Create a floating point value from its representation as a byte array in little endian. + /// Creates a floating point value from its representation as a byte array in little endian. /// /// See [`from_bits`](Self::from_bits) for some discussion of the /// portability of this operation (there are almost no issues). @@ -1358,7 +1370,7 @@ impl f64 { Self::from_bits(u64::from_le_bytes(bytes)) } - /// Create a floating point value from its representation as a byte array in native endian. + /// Creates a floating point value from its representation as a byte array in native endian. /// /// As the target platform's native endianness is used, portable code /// likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as @@ -1388,7 +1400,7 @@ impl f64 { Self::from_bits(u64::from_ne_bytes(bytes)) } - /// Return the ordering between `self` and `other`. + /// Returns the ordering between `self` and `other`. /// /// Unlike the standard partial comparison between floating point numbers, /// this comparison always produces an ordering in accordance to diff --git a/library/core/src/num/flt2dec/mod.rs b/library/core/src/num/flt2dec/mod.rs index 1ff2e8c8228c9..7d923a2652f28 100644 --- a/library/core/src/num/flt2dec/mod.rs +++ b/library/core/src/num/flt2dec/mod.rs @@ -123,7 +123,6 @@ functions. )] pub use self::decoder::{decode, DecodableFloat, Decoded, FullDecoded}; - use super::fmt::{Formatted, Part}; use crate::mem::MaybeUninit; diff --git a/library/core/src/num/flt2dec/strategy/dragon.rs b/library/core/src/num/flt2dec/strategy/dragon.rs index 71b14d0ae3f4c..f8db6370653ab 100644 --- a/library/core/src/num/flt2dec/strategy/dragon.rs +++ b/library/core/src/num/flt2dec/strategy/dragon.rs @@ -6,56 +6,57 @@ use crate::cmp::Ordering; use crate::mem::MaybeUninit; - -use crate::num::bignum::Big32x40 as Big; -use crate::num::bignum::Digit32 as Digit; +use crate::num::bignum::{Big32x40 as Big, Digit32 as Digit}; use crate::num::flt2dec::estimator::estimate_scaling_factor; use crate::num::flt2dec::{round_up, Decoded, MAX_SIG_DIGITS}; static POW10: [Digit; 10] = [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000]; -static TWOPOW10: [Digit; 10] = - [2, 20, 200, 2000, 20000, 200000, 2000000, 20000000, 200000000, 2000000000]; - -// precalculated arrays of `Digit`s for 10^(2^n) -static POW10TO16: [Digit; 2] = [0x6fc10000, 0x2386f2]; -static POW10TO32: [Digit; 4] = [0, 0x85acef81, 0x2d6d415b, 0x4ee]; -static POW10TO64: [Digit; 7] = [0, 0, 0xbf6a1f01, 0x6e38ed64, 0xdaa797ed, 0xe93ff9f4, 0x184f03]; -static POW10TO128: [Digit; 14] = [ - 0, 0, 0, 0, 0x2e953e01, 0x3df9909, 0xf1538fd, 0x2374e42f, 0xd3cff5ec, 0xc404dc08, 0xbccdb0da, - 0xa6337f19, 0xe91f2603, 0x24e, +// precalculated arrays of `Digit`s for 5^(2^n). +static POW5TO16: [Digit; 2] = [0x86f26fc1, 0x23]; +static POW5TO32: [Digit; 3] = [0x85acef81, 0x2d6d415b, 0x4ee]; +static POW5TO64: [Digit; 5] = [0xbf6a1f01, 0x6e38ed64, 0xdaa797ed, 0xe93ff9f4, 0x184f03]; +static POW5TO128: [Digit; 10] = [ + 0x2e953e01, 0x3df9909, 0xf1538fd, 0x2374e42f, 0xd3cff5ec, 0xc404dc08, 0xbccdb0da, 0xa6337f19, + 0xe91f2603, 0x24e, ]; -static POW10TO256: [Digit; 27] = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0x982e7c01, 0xbed3875b, 0xd8d99f72, 0x12152f87, 0x6bde50c6, 0xcf4a6e70, - 0xd595d80f, 0x26b2716e, 0xadc666b0, 0x1d153624, 0x3c42d35a, 0x63ff540e, 0xcc5573c0, 0x65f9ef17, - 0x55bc28f2, 0x80dcc7f7, 0xf46eeddc, 0x5fdcefce, 0x553f7, +static POW5TO256: [Digit; 19] = [ + 0x982e7c01, 0xbed3875b, 0xd8d99f72, 0x12152f87, 0x6bde50c6, 0xcf4a6e70, 0xd595d80f, 0x26b2716e, + 0xadc666b0, 0x1d153624, 0x3c42d35a, 0x63ff540e, 0xcc5573c0, 0x65f9ef17, 0x55bc28f2, 0x80dcc7f7, + 0xf46eeddc, 0x5fdcefce, 0x553f7, ]; #[doc(hidden)] pub fn mul_pow10(x: &mut Big, n: usize) -> &mut Big { debug_assert!(n < 512); + // Save ourself the left shift for the smallest cases. + if n < 8 { + return x.mul_small(POW10[n & 7]); + } + // Multiply by the powers of 5 and shift the 2s in at the end. + // This keeps the intermediate products smaller and faster. if n & 7 != 0 { - x.mul_small(POW10[n & 7]); + x.mul_small(POW10[n & 7] >> (n & 7)); } if n & 8 != 0 { - x.mul_small(POW10[8]); + x.mul_small(POW10[8] >> 8); } if n & 16 != 0 { - x.mul_digits(&POW10TO16); + x.mul_digits(&POW5TO16); } if n & 32 != 0 { - x.mul_digits(&POW10TO32); + x.mul_digits(&POW5TO32); } if n & 64 != 0 { - x.mul_digits(&POW10TO64); + x.mul_digits(&POW5TO64); } if n & 128 != 0 { - x.mul_digits(&POW10TO128); + x.mul_digits(&POW5TO128); } if n & 256 != 0 { - x.mul_digits(&POW10TO256); + x.mul_digits(&POW5TO256); } - x + x.mul_pow2(n) } fn div_2pow10(x: &mut Big, mut n: usize) -> &mut Big { @@ -64,7 +65,7 @@ fn div_2pow10(x: &mut Big, mut n: usize) -> &mut Big { x.div_rem_small(POW10[largest]); n -= largest; } - x.div_rem_small(TWOPOW10[n]); + x.div_rem_small(POW10[n] << 1); x } diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 77b1039039b1d..dd88e859b30e7 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -183,6 +183,30 @@ macro_rules! int_impl { (self as $UnsignedT).trailing_ones() } + /// Returns the bit pattern of `self` reinterpreted as an unsigned integer of the same size. + /// + /// This produces the same result as an `as` cast, but ensures that the bit-width remains + /// the same. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(integer_sign_cast)] + /// + #[doc = concat!("let n = -1", stringify!($SelfT), ";")] + /// + #[doc = concat!("assert_eq!(n.cast_unsigned(), ", stringify!($UnsignedT), "::MAX);")] + /// ``` + #[unstable(feature = "integer_sign_cast", issue = "125882")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn cast_unsigned(self) -> $UnsignedT { + self as $UnsignedT + } + /// Shifts the bits to the left by a specified amount, `n`, /// wrapping the truncated bits to the end of the resulting integer. /// @@ -460,7 +484,7 @@ macro_rules! int_impl { #[track_caller] pub const fn strict_add(self, rhs: Self) -> Self { let (a, b) = self.overflowing_add(rhs); - if unlikely!(b) { overflow_panic::add() } else { a } + if b { overflow_panic::add() } else { a } } /// Unchecked integer addition. Computes `self + rhs`, assuming overflow @@ -488,9 +512,19 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_add`. - unsafe { intrinsics::unchecked_add(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_add cannot overflow"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => !lhs.overflowing_add(rhs).1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_add(self, rhs) + } } /// Checked addition with an unsigned integer. Computes `self + rhs`, @@ -546,7 +580,7 @@ macro_rules! int_impl { #[track_caller] pub const fn strict_add_unsigned(self, rhs: $UnsignedT) -> Self { let (a, b) = self.overflowing_add_unsigned(rhs); - if unlikely!(b) { overflow_panic::add() } else { a } + if b { overflow_panic::add() } else { a } } /// Checked integer subtraction. Computes `self - rhs`, returning `None` if @@ -602,7 +636,7 @@ macro_rules! int_impl { #[track_caller] pub const fn strict_sub(self, rhs: Self) -> Self { let (a, b) = self.overflowing_sub(rhs); - if unlikely!(b) { overflow_panic::sub() } else { a } + if b { overflow_panic::sub() } else { a } } /// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow @@ -630,9 +664,19 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_sub`. - unsafe { intrinsics::unchecked_sub(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_sub cannot overflow"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => !lhs.overflowing_sub(rhs).1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_sub(self, rhs) + } } /// Checked subtraction with an unsigned integer. Computes `self - rhs`, @@ -688,7 +732,7 @@ macro_rules! int_impl { #[track_caller] pub const fn strict_sub_unsigned(self, rhs: $UnsignedT) -> Self { let (a, b) = self.overflowing_sub_unsigned(rhs); - if unlikely!(b) { overflow_panic::sub() } else { a } + if b { overflow_panic::sub() } else { a } } /// Checked integer multiplication. Computes `self * rhs`, returning `None` if @@ -744,7 +788,7 @@ macro_rules! int_impl { #[track_caller] pub const fn strict_mul(self, rhs: Self) -> Self { let (a, b) = self.overflowing_mul(rhs); - if unlikely!(b) { overflow_panic::mul() } else { a } + if b { overflow_panic::mul() } else { a } } /// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow @@ -772,9 +816,19 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_mul`. - unsafe { intrinsics::unchecked_mul(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_mul cannot overflow"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => !lhs.overflowing_mul(rhs).1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_mul(self, rhs) + } } /// Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0` @@ -848,7 +902,7 @@ macro_rules! int_impl { #[track_caller] pub const fn strict_div(self, rhs: Self) -> Self { let (a, b) = self.overflowing_div(rhs); - if unlikely!(b) { overflow_panic::div() } else { a } + if b { overflow_panic::div() } else { a } } /// Checked Euclidean division. Computes `self.div_euclid(rhs)`, @@ -922,7 +976,7 @@ macro_rules! int_impl { #[track_caller] pub const fn strict_div_euclid(self, rhs: Self) -> Self { let (a, b) = self.overflowing_div_euclid(rhs); - if unlikely!(b) { overflow_panic::div() } else { a } + if b { overflow_panic::div() } else { a } } /// Checked integer remainder. Computes `self % rhs`, returning `None` if @@ -995,7 +1049,7 @@ macro_rules! int_impl { #[track_caller] pub const fn strict_rem(self, rhs: Self) -> Self { let (a, b) = self.overflowing_rem(rhs); - if unlikely!(b) { overflow_panic::rem() } else { a } + if b { overflow_panic::rem() } else { a } } /// Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning `None` @@ -1068,7 +1122,7 @@ macro_rules! int_impl { #[track_caller] pub const fn strict_rem_euclid(self, rhs: Self) -> Self { let (a, b) = self.overflowing_rem_euclid(rhs); - if unlikely!(b) { overflow_panic::rem() } else { a } + if b { overflow_panic::rem() } else { a } } /// Checked negation. Computes `-self`, returning `None` if `self == MIN`. @@ -1111,9 +1165,18 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_neg(self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_neg`. - unsafe { intrinsics::unchecked_sub(0, self) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_neg cannot overflow"), + ( + lhs: $SelfT = self, + ) => !lhs.overflowing_neg().1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_sub(0, self) + } } /// Strict negation. Computes `-self`, panicking if `self == MIN`. @@ -1147,7 +1210,7 @@ macro_rules! int_impl { #[track_caller] pub const fn strict_neg(self) -> Self { let (a, b) = self.overflowing_neg(); - if unlikely!(b) { overflow_panic::neg() } else { a } + if b { overflow_panic::neg() } else { a } } /// Checked shift left. Computes `self << rhs`, returning `None` if `rhs` is larger @@ -1160,6 +1223,7 @@ macro_rules! int_impl { /// ``` #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10));")] #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".checked_shl(129), None);")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shl(", stringify!($BITS_MINUS_ONE), "), Some(0));")] /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] @@ -1210,7 +1274,7 @@ macro_rules! int_impl { #[track_caller] pub const fn strict_shl(self, rhs: u32) -> Self { let (a, b) = self.overflowing_shl(rhs); - if unlikely!(b) { overflow_panic::shl() } else { a } + if b { overflow_panic::shl() } else { a } } /// Unchecked shift left. Computes `self << rhs`, assuming that @@ -1234,9 +1298,18 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shl`. - unsafe { intrinsics::unchecked_shl(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_shl cannot overflow"), + ( + rhs: u32 = rhs, + ) => rhs < <$ActualT>::BITS, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_shl(self, rhs) + } } /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is @@ -1299,7 +1372,7 @@ macro_rules! int_impl { #[track_caller] pub const fn strict_shr(self, rhs: u32) -> Self { let (a, b) = self.overflowing_shr(rhs); - if unlikely!(b) { overflow_panic::shr() } else { a } + if b { overflow_panic::shr() } else { a } } /// Unchecked shift right. Computes `self >> rhs`, assuming that @@ -1323,9 +1396,18 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shr`. - unsafe { intrinsics::unchecked_shr(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_shr cannot overflow"), + ( + rhs: u32 = rhs, + ) => rhs < <$ActualT>::BITS, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_shr(self, rhs) + } } /// Checked absolute value. Computes `self.abs()`, returning `None` if @@ -2115,10 +2197,11 @@ macro_rules! int_impl { acc.wrapping_mul(base) } - /// Calculates `self` + `rhs` + /// Calculates `self` + `rhs`. /// - /// Returns a tuple of the addition along with a boolean indicating whether an arithmetic overflow would - /// occur. If an overflow would have occurred then the wrapped value is returned. + /// Returns a tuple of the addition along with a boolean indicating + /// whether an arithmetic overflow would occur. If an overflow would have + /// occurred then the wrapped value is returned. /// /// # Examples /// @@ -2196,7 +2279,7 @@ macro_rules! int_impl { (c, b != d) } - /// Calculates `self` + `rhs` with an unsigned `rhs` + /// Calculates `self` + `rhs` with an unsigned `rhs`. /// /// Returns a tuple of the addition along with a boolean indicating /// whether an arithmetic overflow would occur. If an overflow would @@ -2519,6 +2602,7 @@ macro_rules! int_impl { /// ``` #[doc = concat!("assert_eq!(0x1", stringify!($SelfT),".overflowing_shl(4), (0x10, false));")] /// assert_eq!(0x1i32.overflowing_shl(36), (0x10, true)); + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".overflowing_shl(", stringify!($BITS_MINUS_ONE), "), (0, false));")] /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] @@ -2703,8 +2787,10 @@ macro_rules! int_impl { /// /// In other words, the result is `self / rhs` rounded to the integer `q` /// such that `self >= q * rhs`. - /// If `self > 0`, this is equal to round towards zero (the default in Rust); - /// if `self < 0`, this is equal to round towards +/- infinity. + /// If `self > 0`, this is equal to rounding towards zero (the default in Rust); + /// if `self < 0`, this is equal to rounding away from zero (towards +/- infinity). + /// If `rhs > 0`, this is equal to rounding towards -infinity; + /// if `rhs < 0`, this is equal to rounding towards +infinity. /// /// # Panics /// @@ -2742,8 +2828,8 @@ macro_rules! int_impl { /// Calculates the least nonnegative remainder of `self (mod rhs)`. /// /// This is done as if by the Euclidean division algorithm -- given - /// `r = self.rem_euclid(rhs)`, `self = rhs * self.div_euclid(rhs) + r`, and - /// `0 <= r < abs(rhs)`. + /// `r = self.rem_euclid(rhs)`, the result satisfies + /// `self = rhs * self.div_euclid(rhs) + r` and `0 <= r < abs(rhs)`. /// /// # Panics /// @@ -3306,7 +3392,7 @@ macro_rules! int_impl { #[inline(always)] pub const fn is_negative(self) -> bool { self < 0 } - /// Return the memory representation of this integer as a byte array in + /// Returns the memory representation of this integer as a byte array in /// big-endian (network) byte order. /// #[doc = $to_xe_bytes_doc] @@ -3326,7 +3412,7 @@ macro_rules! int_impl { self.to_be().to_ne_bytes() } - /// Return the memory representation of this integer as a byte array in + /// Returns the memory representation of this integer as a byte array in /// little-endian byte order. /// #[doc = $to_xe_bytes_doc] @@ -3346,7 +3432,7 @@ macro_rules! int_impl { self.to_le().to_ne_bytes() } - /// Return the memory representation of this integer as a byte array in + /// Returns the memory representation of this integer as a byte array in /// native byte order. /// /// As the target platform's native endianness is used, portable code @@ -3384,7 +3470,7 @@ macro_rules! int_impl { unsafe { mem::transmute(self) } } - /// Create an integer value from its representation as a byte array in + /// Creates an integer value from its representation as a byte array in /// big endian. /// #[doc = $from_xe_bytes_doc] @@ -3413,7 +3499,7 @@ macro_rules! int_impl { Self::from_be(Self::from_ne_bytes(bytes)) } - /// Create an integer value from its representation as a byte array in + /// Creates an integer value from its representation as a byte array in /// little endian. /// #[doc = $from_xe_bytes_doc] @@ -3442,7 +3528,7 @@ macro_rules! int_impl { Self::from_le(Self::from_ne_bytes(bytes)) } - /// Create an integer value from its memory representation as a byte + /// Creates an integer value from its memory representation as a byte /// array in native endianness. /// /// As the target platform's native endianness is used, portable code diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 09a341e4d80ac..309e1ba958aee 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -2,11 +2,9 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::ascii; -use crate::hint; -use crate::intrinsics; -use crate::mem; use crate::str::FromStr; +use crate::ub_checks::assert_unsafe_precondition; +use crate::{ascii, intrinsics, mem}; // Used because the `?` operator is not allowed in a const context. macro_rules! try_opt { @@ -48,39 +46,31 @@ mod overflow_panic; mod saturating; mod wrapping; -#[stable(feature = "saturating_int_impl", since = "1.74.0")] -pub use saturating::Saturating; -#[stable(feature = "rust1", since = "1.0.0")] -pub use wrapping::Wrapping; - #[stable(feature = "rust1", since = "1.0.0")] #[cfg(not(no_fp_fmt_parse))] pub use dec2flt::ParseFloatError; - +#[stable(feature = "int_error_matching", since = "1.55.0")] +pub use error::IntErrorKind; #[stable(feature = "rust1", since = "1.0.0")] pub use error::ParseIntError; - +#[stable(feature = "try_from", since = "1.34.0")] +pub use error::TryFromIntError; +#[stable(feature = "generic_nonzero", since = "1.79.0")] +pub use nonzero::NonZero; #[unstable( feature = "nonzero_internals", reason = "implementation detail which may disappear or be replaced at any time", issue = "none" )] pub use nonzero::ZeroablePrimitive; - -#[stable(feature = "generic_nonzero", since = "1.79.0")] -pub use nonzero::NonZero; - #[stable(feature = "signed_nonzero", since = "1.34.0")] pub use nonzero::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize}; - #[stable(feature = "nonzero", since = "1.28.0")] pub use nonzero::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; - -#[stable(feature = "try_from", since = "1.34.0")] -pub use error::TryFromIntError; - -#[stable(feature = "int_error_matching", since = "1.55.0")] -pub use error::IntErrorKind; +#[stable(feature = "saturating_int_impl", since = "1.74.0")] +pub use saturating::Saturating; +#[stable(feature = "rust1", since = "1.0.0")] +pub use wrapping::Wrapping; macro_rules! usize_isize_to_xe_bytes_doc { () => { @@ -482,8 +472,8 @@ impl u8 { Self = u8, ActualT = u8, SignedT = i8, - NonZeroT = NonZero, BITS = 8, + BITS_MINUS_ONE = 7, MAX = 255, rot = 2, rot_op = "0x82", @@ -1097,8 +1087,8 @@ impl u16 { Self = u16, ActualT = u16, SignedT = i16, - NonZeroT = NonZero, BITS = 16, + BITS_MINUS_ONE = 15, MAX = 65535, rot = 4, rot_op = "0xa003", @@ -1146,8 +1136,8 @@ impl u32 { Self = u32, ActualT = u32, SignedT = i32, - NonZeroT = NonZero, BITS = 32, + BITS_MINUS_ONE = 31, MAX = 4294967295, rot = 8, rot_op = "0x10000b3", @@ -1170,8 +1160,8 @@ impl u64 { Self = u64, ActualT = u64, SignedT = i64, - NonZeroT = NonZero, BITS = 64, + BITS_MINUS_ONE = 63, MAX = 18446744073709551615, rot = 12, rot_op = "0xaa00000000006e1", @@ -1194,8 +1184,8 @@ impl u128 { Self = u128, ActualT = u128, SignedT = i128, - NonZeroT = NonZero, BITS = 128, + BITS_MINUS_ONE = 127, MAX = 340282366920938463463374607431768211455, rot = 16, rot_op = "0x13f40000000000000000000000004f76", @@ -1220,8 +1210,8 @@ impl usize { Self = usize, ActualT = u16, SignedT = isize, - NonZeroT = NonZero, BITS = 16, + BITS_MINUS_ONE = 15, MAX = 65535, rot = 4, rot_op = "0xa003", @@ -1245,8 +1235,8 @@ impl usize { Self = usize, ActualT = u32, SignedT = isize, - NonZeroT = NonZero, BITS = 32, + BITS_MINUS_ONE = 31, MAX = 4294967295, rot = 8, rot_op = "0x10000b3", @@ -1270,8 +1260,8 @@ impl usize { Self = usize, ActualT = u64, SignedT = isize, - NonZeroT = NonZero, BITS = 64, + BITS_MINUS_ONE = 63, MAX = 18446744073709551615, rot = 12, rot_op = "0xaa00000000006e1", @@ -1394,6 +1384,7 @@ from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } #[doc(hidden)] #[inline(always)] #[unstable(issue = "none", feature = "std_internals")] +#[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { radix <= 16 && digits.len() <= mem::size_of::() * 2 - is_signed_ty as usize } @@ -1443,7 +1434,7 @@ macro_rules! from_str_radix { #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str_radix(\"A\", 16), Ok(10));")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { use self::IntErrorKind::*; use self::ParseIntError as PIE; @@ -1573,7 +1564,7 @@ macro_rules! from_str_radix_size_impl { #[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")] + #[rustc_const_stable(feature = "const_int_from_str", since = "CURRENT_RUSTC_VERSION")] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> { match <$t>::from_str_radix(src, radix) { Ok(x) => Ok(x as $size), diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index fcdd983343d62..c6e9c249048a7 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1,17 +1,13 @@ //! Definitions of integer that is known not to equal zero. +use super::{IntErrorKind, ParseIntError}; use crate::cmp::Ordering; -use crate::fmt; use crate::hash::{Hash, Hasher}; -use crate::intrinsics; use crate::marker::{Freeze, StructuralPartialEq}; use crate::ops::{BitOr, BitOrAssign, Div, DivAssign, Neg, Rem, RemAssign}; use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::ptr; use crate::str::FromStr; -use crate::ub_checks; - -use super::{IntErrorKind, ParseIntError}; +use crate::{fmt, hint, intrinsics, ptr, ub_checks}; /// A marker trait for primitive types which can be zero. /// @@ -33,7 +29,6 @@ use super::{IntErrorKind, ParseIntError}; reason = "implementation detail which may disappear or be replaced at any time", issue = "none" )] -#[const_trait] pub unsafe trait ZeroablePrimitive: Sized + Copy + private::Sealed { #[doc(hidden)] type NonZeroInner: Sized + Copy; @@ -47,7 +42,6 @@ macro_rules! impl_zeroable_primitive { reason = "implementation detail which may disappear or be replaced at any time", issue = "none" )] - #[const_trait] pub trait Sealed {} $( @@ -70,14 +64,14 @@ macro_rules! impl_zeroable_primitive { reason = "implementation detail which may disappear or be replaced at any time", issue = "none" )] - impl const private::Sealed for $primitive {} + impl private::Sealed for $primitive {} #[unstable( feature = "nonzero_internals", reason = "implementation detail which may disappear or be replaced at any time", issue = "none" )] - unsafe impl const ZeroablePrimitive for $primitive { + unsafe impl ZeroablePrimitive for $primitive { type NonZeroInner = private::$NonZeroInner; } )+ @@ -456,6 +450,12 @@ macro_rules! nonzero_integer { UnsignedPrimitive = $Uint:ty, // Used in doc comments. + rot = $rot:literal, + rot_op = $rot_op:literal, + rot_result = $rot_result:literal, + swap_op = $swap_op:literal, + swapped = $swapped:literal, + reversed = $reversed:literal, leading_zeros_test = $leading_zeros_test:expr, ) => { /// An integer that is known not to equal zero. @@ -517,9 +517,13 @@ macro_rules! nonzero_integer { /// ``` /// # use std::num::NonZero; /// # - #[doc = concat!("let n = NonZero::<", stringify!($Int), ">::new(", $leading_zeros_test, ").unwrap();")] + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let n = NonZero::<", stringify!($Int), ">::new(", $leading_zeros_test, ")?;")] /// /// assert_eq!(n.leading_zeros(), 0); + /// # Some(()) + /// # } /// ``` #[stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] #[rustc_const_stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] @@ -545,9 +549,13 @@ macro_rules! nonzero_integer { /// ``` /// # use std::num::NonZero; /// # - #[doc = concat!("let n = NonZero::<", stringify!($Int), ">::new(0b0101000).unwrap();")] + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let n = NonZero::<", stringify!($Int), ">::new(0b0101000)?;")] /// /// assert_eq!(n.trailing_zeros(), 3); + /// # Some(()) + /// # } /// ``` #[stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] #[rustc_const_stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] @@ -597,8 +605,271 @@ macro_rules! nonzero_integer { unsafe { NonZero::new_unchecked(self.get().count_ones()) } } + /// Shifts the bits to the left by a specified amount, `n`, + /// wrapping the truncated bits to the end of the resulting integer. + /// + /// Please note this isn't the same operation as the `<<` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(nonzero_bitwise)] + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let n = NonZero::new(", $rot_op, stringify!($Int), ")?;")] + #[doc = concat!("let m = NonZero::new(", $rot_result, ")?;")] + /// + #[doc = concat!("assert_eq!(n.rotate_left(", $rot, "), m);")] + /// # Some(()) + /// # } + /// ``` + #[unstable(feature = "nonzero_bitwise", issue = "128281")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn rotate_left(self, n: u32) -> Self { + let result = self.get().rotate_left(n); + // SAFETY: Rotating bits preserves the property int > 0. + unsafe { Self::new_unchecked(result) } + } + + /// Shifts the bits to the right by a specified amount, `n`, + /// wrapping the truncated bits to the beginning of the resulting + /// integer. + /// + /// Please note this isn't the same operation as the `>>` shifting operator! + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(nonzero_bitwise)] + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let n = NonZero::new(", $rot_result, stringify!($Int), ")?;")] + #[doc = concat!("let m = NonZero::new(", $rot_op, ")?;")] + /// + #[doc = concat!("assert_eq!(n.rotate_right(", $rot, "), m);")] + /// # Some(()) + /// # } + /// ``` + #[unstable(feature = "nonzero_bitwise", issue = "128281")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn rotate_right(self, n: u32) -> Self { + let result = self.get().rotate_right(n); + // SAFETY: Rotating bits preserves the property int > 0. + unsafe { Self::new_unchecked(result) } + } + + /// Reverses the byte order of the integer. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(nonzero_bitwise)] + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let n = NonZero::new(", $swap_op, stringify!($Int), ")?;")] + /// let m = n.swap_bytes(); + /// + #[doc = concat!("assert_eq!(m, NonZero::new(", $swapped, ")?);")] + /// # Some(()) + /// # } + /// ``` + #[unstable(feature = "nonzero_bitwise", issue = "128281")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn swap_bytes(self) -> Self { + let result = self.get().swap_bytes(); + // SAFETY: Shuffling bytes preserves the property int > 0. + unsafe { Self::new_unchecked(result) } + } + + /// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, + /// second least-significant bit becomes second most-significant bit, etc. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(nonzero_bitwise)] + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let n = NonZero::new(", $swap_op, stringify!($Int), ")?;")] + /// let m = n.reverse_bits(); + /// + #[doc = concat!("assert_eq!(m, NonZero::new(", $reversed, ")?);")] + /// # Some(()) + /// # } + /// ``` + #[unstable(feature = "nonzero_bitwise", issue = "128281")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn reverse_bits(self) -> Self { + let result = self.get().reverse_bits(); + // SAFETY: Reversing bits preserves the property int > 0. + unsafe { Self::new_unchecked(result) } + } + + /// Converts an integer from big endian to the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(nonzero_bitwise)] + /// # use std::num::NonZero; + #[doc = concat!("use std::num::", stringify!($Ty), ";")] + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let n = NonZero::new(0x1A", stringify!($Int), ")?;")] + /// + /// if cfg!(target_endian = "big") { + #[doc = concat!(" assert_eq!(", stringify!($Ty), "::from_be(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(", stringify!($Ty), "::from_be(n), n.swap_bytes())")] + /// } + /// # Some(()) + /// # } + /// ``` + #[unstable(feature = "nonzero_bitwise", issue = "128281")] + #[must_use] + #[inline(always)] + pub const fn from_be(x: Self) -> Self { + let result = $Int::from_be(x.get()); + // SAFETY: Shuffling bytes preserves the property int > 0. + unsafe { Self::new_unchecked(result) } + } + + /// Converts an integer from little endian to the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(nonzero_bitwise)] + /// # use std::num::NonZero; + #[doc = concat!("use std::num::", stringify!($Ty), ";")] + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let n = NonZero::new(0x1A", stringify!($Int), ")?;")] + /// + /// if cfg!(target_endian = "little") { + #[doc = concat!(" assert_eq!(", stringify!($Ty), "::from_le(n), n)")] + /// } else { + #[doc = concat!(" assert_eq!(", stringify!($Ty), "::from_le(n), n.swap_bytes())")] + /// } + /// # Some(()) + /// # } + /// ``` + #[unstable(feature = "nonzero_bitwise", issue = "128281")] + #[must_use] + #[inline(always)] + pub const fn from_le(x: Self) -> Self { + let result = $Int::from_le(x.get()); + // SAFETY: Shuffling bytes preserves the property int > 0. + unsafe { Self::new_unchecked(result) } + } + + /// Converts `self` to big endian from the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(nonzero_bitwise)] + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let n = NonZero::new(0x1A", stringify!($Int), ")?;")] + /// + /// if cfg!(target_endian = "big") { + /// assert_eq!(n.to_be(), n) + /// } else { + /// assert_eq!(n.to_be(), n.swap_bytes()) + /// } + /// # Some(()) + /// # } + /// ``` + #[unstable(feature = "nonzero_bitwise", issue = "128281")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn to_be(self) -> Self { + let result = self.get().to_be(); + // SAFETY: Shuffling bytes preserves the property int > 0. + unsafe { Self::new_unchecked(result) } + } + + /// Converts `self` to little endian from the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are + /// swapped. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(nonzero_bitwise)] + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let n = NonZero::new(0x1A", stringify!($Int), ")?;")] + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(n.to_le(), n) + /// } else { + /// assert_eq!(n.to_le(), n.swap_bytes()) + /// } + /// # Some(()) + /// # } + /// ``` + #[unstable(feature = "nonzero_bitwise", issue = "128281")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn to_le(self) -> Self { + let result = self.get().to_le(); + // SAFETY: Shuffling bytes preserves the property int > 0. + unsafe { Self::new_unchecked(result) } + } + nonzero_integer_signedness_dependent_methods! { - Self = $Ty, Primitive = $signedness $Int, UnsignedPrimitive = $Uint, } @@ -817,25 +1088,57 @@ macro_rules! nonzero_integer { } } - nonzero_integer_signedness_dependent_impls!($Ty $signedness $Int); + nonzero_integer_signedness_dependent_impls!($signedness $Int); }; - (Self = $Ty:ident, Primitive = unsigned $Int:ident $(,)?) => { + ( + Self = $Ty:ident, + Primitive = unsigned $Int:ident, + rot = $rot:literal, + rot_op = $rot_op:literal, + rot_result = $rot_result:literal, + swap_op = $swap_op:literal, + swapped = $swapped:literal, + reversed = $reversed:literal, + $(,)? + ) => { nonzero_integer! { #[stable(feature = "nonzero", since = "1.28.0")] Self = $Ty, Primitive = unsigned $Int, UnsignedPrimitive = $Int, + rot = $rot, + rot_op = $rot_op, + rot_result = $rot_result, + swap_op = $swap_op, + swapped = $swapped, + reversed = $reversed, leading_zeros_test = concat!(stringify!($Int), "::MAX"), } }; - (Self = $Ty:ident, Primitive = signed $Int:ident, $($rest:tt)*) => { + ( + Self = $Ty:ident, + Primitive = signed $Int:ident, + UnsignedPrimitive = $UInt:ident, + rot = $rot:literal, + rot_op = $rot_op:literal, + rot_result = $rot_result:literal, + swap_op = $swap_op:literal, + swapped = $swapped:literal, + reversed = $reversed:literal, + ) => { nonzero_integer! { #[stable(feature = "signed_nonzero", since = "1.34.0")] Self = $Ty, Primitive = signed $Int, - $($rest)* + UnsignedPrimitive = $UInt, + rot = $rot, + rot_op = $rot_op, + rot_result = $rot_result, + swap_op = $swap_op, + swapped = $swapped, + reversed = $reversed, leading_zeros_test = concat!("-1", stringify!($Int)), } }; @@ -843,7 +1146,7 @@ macro_rules! nonzero_integer { macro_rules! nonzero_integer_signedness_dependent_impls { // Impls for unsigned nonzero types only. - ($Ty:ident unsigned $Int:ty) => { + (unsigned $Int:ty) => { #[stable(feature = "nonzero_div", since = "1.51.0")] impl Div> for $Int { type Output = $Int; @@ -891,7 +1194,7 @@ macro_rules! nonzero_integer_signedness_dependent_impls { } }; // Impls for signed nonzero types only. - ($Ty:ident signed $Int:ty) => { + (signed $Int:ty) => { #[stable(feature = "signed_nonzero_neg", since = "1.71.0")] impl Neg for NonZero<$Int> { type Output = Self; @@ -912,7 +1215,6 @@ macro_rules! nonzero_integer_signedness_dependent_impls { macro_rules! nonzero_integer_signedness_dependent_methods { // Associated items for unsigned nonzero types only. ( - Self = $Ty:ident, Primitive = unsigned $Int:ident, UnsignedPrimitive = $Uint:ty, ) => { @@ -1051,7 +1353,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { unsafe { Self::new_unchecked(self.get().unchecked_add(other)) } } - /// Returns the smallest power of two greater than or equal to n. + /// Returns the smallest power of two greater than or equal to `self`. /// Checks for overflow and returns [`None`] /// if the next power of two is greater than the type’s maximum value. /// As a consequence, the result cannot wrap to zero. @@ -1101,9 +1403,13 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// ``` /// # use std::num::NonZero; /// # - #[doc = concat!("assert_eq!(NonZero::new(7", stringify!($Int), ").unwrap().ilog2(), 2);")] - #[doc = concat!("assert_eq!(NonZero::new(8", stringify!($Int), ").unwrap().ilog2(), 3);")] - #[doc = concat!("assert_eq!(NonZero::new(9", stringify!($Int), ").unwrap().ilog2(), 3);")] + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("assert_eq!(NonZero::new(7", stringify!($Int), ")?.ilog2(), 2);")] + #[doc = concat!("assert_eq!(NonZero::new(8", stringify!($Int), ")?.ilog2(), 3);")] + #[doc = concat!("assert_eq!(NonZero::new(9", stringify!($Int), ")?.ilog2(), 3);")] + /// # Some(()) + /// # } /// ``` #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] @@ -1126,9 +1432,13 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// ``` /// # use std::num::NonZero; /// # - #[doc = concat!("assert_eq!(NonZero::new(99", stringify!($Int), ").unwrap().ilog10(), 1);")] - #[doc = concat!("assert_eq!(NonZero::new(100", stringify!($Int), ").unwrap().ilog10(), 2);")] - #[doc = concat!("assert_eq!(NonZero::new(101", stringify!($Int), ").unwrap().ilog10(), 2);")] + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("assert_eq!(NonZero::new(99", stringify!($Int), ")?.ilog10(), 1);")] + #[doc = concat!("assert_eq!(NonZero::new(100", stringify!($Int), ")?.ilog10(), 2);")] + #[doc = concat!("assert_eq!(NonZero::new(101", stringify!($Int), ")?.ilog10(), 2);")] + /// # Some(()) + /// # } /// ``` #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] @@ -1187,10 +1497,16 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// Basic usage: /// /// ``` - #[doc = concat!("let eight = std::num::NonZero::new(8", stringify!($Int), ").unwrap();")] + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let eight = NonZero::new(8", stringify!($Int), ")?;")] /// assert!(eight.is_power_of_two()); - #[doc = concat!("let ten = std::num::NonZero::new(10", stringify!($Int), ").unwrap();")] + #[doc = concat!("let ten = NonZero::new(10", stringify!($Int), ")?;")] /// assert!(!ten.is_power_of_two()); + /// # Some(()) + /// # } /// ``` #[must_use] #[stable(feature = "nonzero_is_power_of_two", since = "1.59.0")] @@ -1204,11 +1520,61 @@ macro_rules! nonzero_integer_signedness_dependent_methods { intrinsics::ctpop(self.get()) < 2 } + + /// Returns the square root of the number, rounded down. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(isqrt)] + /// # use std::num::NonZero; + /// # + /// # fn main() { test().unwrap(); } + /// # fn test() -> Option<()> { + #[doc = concat!("let ten = NonZero::new(10", stringify!($Int), ")?;")] + #[doc = concat!("let three = NonZero::new(3", stringify!($Int), ")?;")] + /// + /// assert_eq!(ten.isqrt(), three); + /// # Some(()) + /// # } + /// ``` + #[unstable(feature = "isqrt", issue = "116226")] + #[rustc_const_unstable(feature = "isqrt", issue = "116226")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn isqrt(self) -> Self { + // The algorithm is based on the one presented in + // + // which cites as source the following C code: + // . + + let mut op = self.get(); + let mut res = 0; + let mut one = 1 << (self.ilog2() & !1); + + while one != 0 { + if op >= res + one { + op -= res + one; + res = (res >> 1) + one; + } else { + res >>= 1; + } + one >>= 2; + } + + // SAFETY: The result fits in an integer with half as many bits. + // Inform the optimizer about it. + unsafe { hint::assert_unchecked(res < 1 << (Self::BITS / 2)) }; + + // SAFETY: The square root of an integer >= 1 is always >= 1. + unsafe { Self::new_unchecked(res) } + } }; // Associated items for signed nonzero types only. ( - Self = $Ty:ident, Primitive = signed $Int:ident, UnsignedPrimitive = $Uint:ty, ) => { @@ -1636,65 +2002,189 @@ macro_rules! sign_dependent_expr { nonzero_integer! { Self = NonZeroU8, Primitive = unsigned u8, + rot = 2, + rot_op = "0x82", + rot_result = "0xa", + swap_op = "0x12", + swapped = "0x12", + reversed = "0x48", } nonzero_integer! { Self = NonZeroU16, Primitive = unsigned u16, + rot = 4, + rot_op = "0xa003", + rot_result = "0x3a", + swap_op = "0x1234", + swapped = "0x3412", + reversed = "0x2c48", } nonzero_integer! { Self = NonZeroU32, Primitive = unsigned u32, + rot = 8, + rot_op = "0x10000b3", + rot_result = "0xb301", + swap_op = "0x12345678", + swapped = "0x78563412", + reversed = "0x1e6a2c48", } nonzero_integer! { Self = NonZeroU64, Primitive = unsigned u64, + rot = 12, + rot_op = "0xaa00000000006e1", + rot_result = "0x6e10aa", + swap_op = "0x1234567890123456", + swapped = "0x5634129078563412", + reversed = "0x6a2c48091e6a2c48", } nonzero_integer! { Self = NonZeroU128, Primitive = unsigned u128, + rot = 16, + rot_op = "0x13f40000000000000000000000004f76", + rot_result = "0x4f7613f4", + swap_op = "0x12345678901234567890123456789012", + swapped = "0x12907856341290785634129078563412", + reversed = "0x48091e6a2c48091e6a2c48091e6a2c48", } +#[cfg(target_pointer_width = "16")] nonzero_integer! { Self = NonZeroUsize, Primitive = unsigned usize, + rot = 4, + rot_op = "0xa003", + rot_result = "0x3a", + swap_op = "0x1234", + swapped = "0x3412", + reversed = "0x2c48", +} + +#[cfg(target_pointer_width = "32")] +nonzero_integer! { + Self = NonZeroUsize, + Primitive = unsigned usize, + rot = 8, + rot_op = "0x10000b3", + rot_result = "0xb301", + swap_op = "0x12345678", + swapped = "0x78563412", + reversed = "0x1e6a2c48", +} + +#[cfg(target_pointer_width = "64")] +nonzero_integer! { + Self = NonZeroUsize, + Primitive = unsigned usize, + rot = 12, + rot_op = "0xaa00000000006e1", + rot_result = "0x6e10aa", + swap_op = "0x1234567890123456", + swapped = "0x5634129078563412", + reversed = "0x6a2c48091e6a2c48", } nonzero_integer! { Self = NonZeroI8, Primitive = signed i8, UnsignedPrimitive = u8, + rot = 2, + rot_op = "-0x7e", + rot_result = "0xa", + swap_op = "0x12", + swapped = "0x12", + reversed = "0x48", } nonzero_integer! { Self = NonZeroI16, Primitive = signed i16, UnsignedPrimitive = u16, + rot = 4, + rot_op = "-0x5ffd", + rot_result = "0x3a", + swap_op = "0x1234", + swapped = "0x3412", + reversed = "0x2c48", } nonzero_integer! { Self = NonZeroI32, Primitive = signed i32, UnsignedPrimitive = u32, + rot = 8, + rot_op = "0x10000b3", + rot_result = "0xb301", + swap_op = "0x12345678", + swapped = "0x78563412", + reversed = "0x1e6a2c48", } nonzero_integer! { Self = NonZeroI64, Primitive = signed i64, UnsignedPrimitive = u64, + rot = 12, + rot_op = "0xaa00000000006e1", + rot_result = "0x6e10aa", + swap_op = "0x1234567890123456", + swapped = "0x5634129078563412", + reversed = "0x6a2c48091e6a2c48", } nonzero_integer! { Self = NonZeroI128, Primitive = signed i128, UnsignedPrimitive = u128, + rot = 16, + rot_op = "0x13f40000000000000000000000004f76", + rot_result = "0x4f7613f4", + swap_op = "0x12345678901234567890123456789012", + swapped = "0x12907856341290785634129078563412", + reversed = "0x48091e6a2c48091e6a2c48091e6a2c48", +} + +#[cfg(target_pointer_width = "16")] +nonzero_integer! { + Self = NonZeroIsize, + Primitive = signed isize, + UnsignedPrimitive = usize, + rot = 4, + rot_op = "-0x5ffd", + rot_result = "0x3a", + swap_op = "0x1234", + swapped = "0x3412", + reversed = "0x2c48", +} + +#[cfg(target_pointer_width = "32")] +nonzero_integer! { + Self = NonZeroIsize, + Primitive = signed isize, + UnsignedPrimitive = usize, + rot = 8, + rot_op = "0x10000b3", + rot_result = "0xb301", + swap_op = "0x12345678", + swapped = "0x78563412", + reversed = "0x1e6a2c48", } +#[cfg(target_pointer_width = "64")] nonzero_integer! { Self = NonZeroIsize, Primitive = signed isize, UnsignedPrimitive = usize, + rot = 12, + rot_op = "0xaa00000000006e1", + rot_result = "0x6e10aa", + swap_op = "0x1234567890123456", + swapped = "0x5634129078563412", + reversed = "0x6a2c48091e6a2c48", } diff --git a/library/core/src/num/saturating.rs b/library/core/src/num/saturating.rs index d040539ebe556..3f4791e163e69 100644 --- a/library/core/src/num/saturating.rs +++ b/library/core/src/num/saturating.rs @@ -1,10 +1,10 @@ //! Definitions of `Saturating`. use crate::fmt; -use crate::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign}; -use crate::ops::{BitXor, BitXorAssign, Div, DivAssign}; -use crate::ops::{Mul, MulAssign, Neg, Not, Rem, RemAssign}; -use crate::ops::{Sub, SubAssign}; +use crate::ops::{ + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, + Mul, MulAssign, Neg, Not, Rem, RemAssign, Sub, SubAssign, +}; /// Provides intentionally-saturating arithmetic on `T`. /// diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 446d0658c1262..a2e17fae76873 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -3,13 +3,13 @@ macro_rules! uint_impl { Self = $SelfT:ty, ActualT = $ActualT:ident, SignedT = $SignedT:ident, - NonZeroT = $NonZeroT:ty, // There are all for use *only* in doc comments. // As such, they're all passed as literals -- passing them as a string // literal is fine if they need to be multiple code tokens. // In non-comments, use the associated constants rather than these. BITS = $BITS:literal, + BITS_MINUS_ONE = $BITS_MINUS_ONE:literal, MAX = $MaxV:literal, rot = $rot:literal, rot_op = $rot_op:literal, @@ -66,8 +66,13 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("let n = 0b01001100", stringify!($SelfT), ";")] - /// /// assert_eq!(n.count_ones(), 3); + /// + #[doc = concat!("let max = ", stringify!($SelfT),"::MAX;")] + #[doc = concat!("assert_eq!(max.count_ones(), ", stringify!($BITS), ");")] + /// + #[doc = concat!("let zero = 0", stringify!($SelfT), ";")] + /// assert_eq!(zero.count_ones(), 0); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] @@ -87,7 +92,11 @@ macro_rules! uint_impl { /// Basic usage: /// /// ``` - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.count_zeros(), 0);")] + #[doc = concat!("let zero = 0", stringify!($SelfT), ";")] + #[doc = concat!("assert_eq!(zero.count_zeros(), ", stringify!($BITS), ");")] + /// + #[doc = concat!("let max = ", stringify!($SelfT),"::MAX;")] + /// assert_eq!(max.count_zeros(), 0); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] @@ -109,8 +118,13 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("let n = ", stringify!($SelfT), "::MAX >> 2;")] - /// /// assert_eq!(n.leading_zeros(), 2); + /// + #[doc = concat!("let zero = 0", stringify!($SelfT), ";")] + #[doc = concat!("assert_eq!(zero.leading_zeros(), ", stringify!($BITS), ");")] + /// + #[doc = concat!("let max = ", stringify!($SelfT),"::MAX;")] + /// assert_eq!(max.leading_zeros(), 0); /// ``` #[doc = concat!("[`ilog2`]: ", stringify!($SelfT), "::ilog2")] #[stable(feature = "rust1", since = "1.0.0")] @@ -131,8 +145,13 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("let n = 0b0101000", stringify!($SelfT), ";")] - /// /// assert_eq!(n.trailing_zeros(), 3); + /// + #[doc = concat!("let zero = 0", stringify!($SelfT), ";")] + #[doc = concat!("assert_eq!(zero.trailing_zeros(), ", stringify!($BITS), ");")] + /// + #[doc = concat!("let max = ", stringify!($SelfT),"::MAX;")] + #[doc = concat!("assert_eq!(max.trailing_zeros(), 0);")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] @@ -151,8 +170,13 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("let n = !(", stringify!($SelfT), "::MAX >> 2);")] - /// /// assert_eq!(n.leading_ones(), 2); + /// + #[doc = concat!("let zero = 0", stringify!($SelfT), ";")] + /// assert_eq!(zero.leading_ones(), 0); + /// + #[doc = concat!("let max = ", stringify!($SelfT),"::MAX;")] + #[doc = concat!("assert_eq!(max.leading_ones(), ", stringify!($BITS), ");")] /// ``` #[stable(feature = "leading_trailing_ones", since = "1.46.0")] #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] @@ -172,8 +196,13 @@ macro_rules! uint_impl { /// /// ``` #[doc = concat!("let n = 0b1010111", stringify!($SelfT), ";")] - /// /// assert_eq!(n.trailing_ones(), 3); + /// + #[doc = concat!("let zero = 0", stringify!($SelfT), ";")] + /// assert_eq!(zero.trailing_ones(), 0); + /// + #[doc = concat!("let max = ", stringify!($SelfT),"::MAX;")] + #[doc = concat!("assert_eq!(max.trailing_ones(), ", stringify!($BITS), ");")] /// ``` #[stable(feature = "leading_trailing_ones", since = "1.46.0")] #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] @@ -184,6 +213,30 @@ macro_rules! uint_impl { (!self).trailing_zeros() } + /// Returns the bit pattern of `self` reinterpreted as a signed integer of the same size. + /// + /// This produces the same result as an `as` cast, but ensures that the bit-width remains + /// the same. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(integer_sign_cast)] + /// + #[doc = concat!("let n = ", stringify!($SelfT), "::MAX;")] + /// + #[doc = concat!("assert_eq!(n.cast_signed(), -1", stringify!($SignedT), ");")] + /// ``` + #[unstable(feature = "integer_sign_cast", issue = "125882")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn cast_signed(self) -> $SignedT { + self as $SignedT + } + /// Shifts the bits to the left by a specified amount, `n`, /// wrapping the truncated bits to the end of the resulting integer. /// @@ -431,8 +484,19 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn checked_add(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_add(rhs); - if unlikely!(b) { None } else { Some(a) } + // This used to use `overflowing_add`, but that means it ends up being + // a `wrapping_add`, losing some optimization opportunities. Notably, + // phrasing it this way helps `.checked_add(1)` optimize to a check + // against `MAX` and a `add nuw`. + // Per , + // LLVM is happy to re-form the intrinsic later if useful. + + if unlikely!(intrinsics::add_with_overflow(self, rhs).1) { + None + } else { + // SAFETY: Just checked it doesn't overflow + Some(unsafe { intrinsics::unchecked_add(self, rhs) }) + } } /// Strict integer addition. Computes `self + rhs`, panicking @@ -467,7 +531,7 @@ macro_rules! uint_impl { #[track_caller] pub const fn strict_add(self, rhs: Self) -> Self { let (a, b) = self.overflowing_add(rhs); - if unlikely!(b) { overflow_panic ::add()} else {a} + if b { overflow_panic::add() } else { a } } /// Unchecked integer addition. Computes `self + rhs`, assuming overflow @@ -495,9 +559,19 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_add`. - unsafe { intrinsics::unchecked_add(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_add cannot overflow"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => !lhs.overflowing_add(rhs).1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_add(self, rhs) + } } /// Checked addition with a signed integer. Computes `self + rhs`, @@ -559,7 +633,7 @@ macro_rules! uint_impl { #[track_caller] pub const fn strict_add_signed(self, rhs: $SignedT) -> Self { let (a, b) = self.overflowing_add_signed(rhs); - if unlikely!(b) { overflow_panic ::add()} else {a} + if b { overflow_panic::add() } else { a } } /// Checked integer subtraction. Computes `self - rhs`, returning @@ -624,7 +698,7 @@ macro_rules! uint_impl { #[track_caller] pub const fn strict_sub(self, rhs: Self) -> Self { let (a, b) = self.overflowing_sub(rhs); - if unlikely!(b) { overflow_panic ::sub()} else {a} + if b { overflow_panic::sub() } else { a } } /// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow @@ -677,9 +751,80 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_sub`. - unsafe { intrinsics::unchecked_sub(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_sub cannot overflow"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => !lhs.overflowing_sub(rhs).1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_sub(self, rhs) + } + } + + #[doc = concat!( + "Checked integer subtraction. Computes `self - rhs` and checks if the result fits into an [`", + stringify!($SignedT), "`], returning `None` if overflow occurred." + )] + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(unsigned_signed_diff)] + #[doc = concat!("assert_eq!(10", stringify!($SelfT), ".checked_signed_diff(2), Some(8));")] + #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".checked_signed_diff(10), Some(-8));")] + #[doc = concat!( + "assert_eq!(", + stringify!($SelfT), + "::MAX.checked_signed_diff(", + stringify!($SignedT), + "::MAX as ", + stringify!($SelfT), + "), None);" + )] + #[doc = concat!( + "assert_eq!((", + stringify!($SignedT), + "::MAX as ", + stringify!($SelfT), + ").checked_signed_diff(", + stringify!($SelfT), + "::MAX), Some(", + stringify!($SignedT), + "::MIN));" + )] + #[doc = concat!( + "assert_eq!((", + stringify!($SignedT), + "::MAX as ", + stringify!($SelfT), + " + 1).checked_signed_diff(0), None);" + )] + #[doc = concat!( + "assert_eq!(", + stringify!($SelfT), + "::MAX.checked_signed_diff(", + stringify!($SelfT), + "::MAX), Some(0));" + )] + /// ``` + #[unstable(feature = "unsigned_signed_diff", issue = "126041")] + #[inline] + pub const fn checked_signed_diff(self, rhs: Self) -> Option<$SignedT> { + let res = self.wrapping_sub(rhs) as $SignedT; + let overflow = (self >= rhs) == (res < 0); + + if !overflow { + Some(res) + } else { + None + } } /// Checked integer multiplication. Computes `self * rhs`, returning @@ -735,7 +880,7 @@ macro_rules! uint_impl { #[track_caller] pub const fn strict_mul(self, rhs: Self) -> Self { let (a, b) = self.overflowing_mul(rhs); - if unlikely!(b) { overflow_panic ::mul()} else {a} + if b { overflow_panic::mul() } else { a } } /// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow @@ -763,9 +908,19 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_mul`. - unsafe { intrinsics::unchecked_mul(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_mul cannot overflow"), + ( + lhs: $SelfT = self, + rhs: $SelfT = rhs, + ) => !lhs.overflowing_mul(rhs).1, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_mul(self, rhs) + } } /// Checked integer division. Computes `self / rhs`, returning `None` @@ -795,10 +950,10 @@ macro_rules! uint_impl { } /// Strict integer division. Computes `self / rhs`. - /// Strict division on unsigned types is just normal division. - /// There's no way overflow could ever happen. - /// This function exists, so that all operations - /// are accounted for in the strict operations. + /// + /// Strict division on unsigned types is just normal division. There's no + /// way overflow could ever happen. This function exists so that all + /// operations are accounted for in the strict operations. /// /// # Panics /// @@ -854,12 +1009,11 @@ macro_rules! uint_impl { } /// Strict Euclidean division. Computes `self.div_euclid(rhs)`. - /// Strict division on unsigned types is just normal division. - /// There's no way overflow could ever happen. - /// This function exists, so that all operations - /// are accounted for in the strict operations. - /// Since, for the positive integers, all common - /// definitions of division are equal, this + /// + /// Strict division on unsigned types is just normal division. There's no + /// way overflow could ever happen. This function exists so that all + /// operations are accounted for in the strict operations. Since, for the + /// positive integers, all common definitions of division are equal, this /// is exactly equal to `self.strict_div(rhs)`. /// /// # Panics @@ -917,11 +1071,11 @@ macro_rules! uint_impl { } /// Strict integer remainder. Computes `self % rhs`. - /// Strict remainder calculation on unsigned types is - /// just the regular remainder calculation. - /// There's no way overflow could ever happen. - /// This function exists, so that all operations - /// are accounted for in the strict operations. + /// + /// Strict remainder calculation on unsigned types is just the regular + /// remainder calculation. There's no way overflow could ever happen. + /// This function exists so that all operations are accounted for in the + /// strict operations. /// /// # Panics /// @@ -977,14 +1131,13 @@ macro_rules! uint_impl { } /// Strict Euclidean modulo. Computes `self.rem_euclid(rhs)`. - /// Strict modulo calculation on unsigned types is - /// just the regular remainder calculation. - /// There's no way overflow could ever happen. - /// This function exists, so that all operations - /// are accounted for in the strict operations. - /// Since, for the positive integers, all common - /// definitions of division are equal, this - /// is exactly equal to `self.strict_rem(rhs)`. + /// + /// Strict modulo calculation on unsigned types is just the regular + /// remainder calculation. There's no way overflow could ever happen. + /// This function exists so that all operations are accounted for in the + /// strict operations. Since, for the positive integers, all common + /// definitions of division are equal, this is exactly equal to + /// `self.strict_rem(rhs)`. /// /// # Panics /// @@ -1118,9 +1271,12 @@ macro_rules! uint_impl { pub const fn checked_ilog(self, base: Self) -> Option { if self <= 0 || base <= 1 { None + } else if self < base { + Some(0) } else { - let mut n = 0; - let mut r = 1; + // Since base >= self, n >= 1 + let mut n = 1; + let mut r = base; // Optimization for 128 bit wide integers. if Self::BITS == 128 { @@ -1159,11 +1315,9 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn checked_ilog2(self) -> Option { - // FIXME: Simply use `NonZero::new` once it is actually generic. - if let Some(x) = <$NonZeroT>::new(self) { - Some(x.ilog2()) - } else { - None + match NonZero::new(self) { + Some(x) => Some(x.ilog2()), + None => None, } } @@ -1182,11 +1336,9 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn checked_ilog10(self) -> Option { - // FIXME: Simply use `NonZero::new` once it is actually generic. - if let Some(x) = <$NonZeroT>::new(self) { - Some(x.ilog10()) - } else { - None + match NonZero::new(self) { + Some(x) => Some(x.ilog10()), + None => None, } } @@ -1247,7 +1399,7 @@ macro_rules! uint_impl { #[track_caller] pub const fn strict_neg(self) -> Self { let (a, b) = self.overflowing_neg(); - if unlikely!(b) { overflow_panic::neg() } else { a } + if b { overflow_panic::neg() } else { a } } /// Checked shift left. Computes `self << rhs`, returning `None` @@ -1260,6 +1412,7 @@ macro_rules! uint_impl { /// ``` #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".checked_shl(4), Some(0x10));")] #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shl(129), None);")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".checked_shl(", stringify!($BITS_MINUS_ONE), "), Some(0));")] /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] @@ -1310,7 +1463,7 @@ macro_rules! uint_impl { #[track_caller] pub const fn strict_shl(self, rhs: u32) -> Self { let (a, b) = self.overflowing_shl(rhs); - if unlikely!(b) { overflow_panic::shl() } else { a } + if b { overflow_panic::shl() } else { a } } /// Unchecked shift left. Computes `self << rhs`, assuming that @@ -1334,9 +1487,18 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shl`. - unsafe { intrinsics::unchecked_shl(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_shl cannot overflow"), + ( + rhs: u32 = rhs, + ) => rhs < <$ActualT>::BITS, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_shl(self, rhs) + } } /// Checked shift right. Computes `self >> rhs`, returning `None` @@ -1399,7 +1561,7 @@ macro_rules! uint_impl { #[track_caller] pub const fn strict_shr(self, rhs: u32) -> Self { let (a, b) = self.overflowing_shr(rhs); - if unlikely!(b) { overflow_panic::shr() } else { a } + if b { overflow_panic::shr() } else { a } } /// Unchecked shift right. Computes `self >> rhs`, assuming that @@ -1423,9 +1585,18 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shr`. - unsafe { intrinsics::unchecked_shr(self, rhs) } + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_shr cannot overflow"), + ( + rhs: u32 = rhs, + ) => rhs < <$ActualT>::BITS, + ); + + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + intrinsics::unchecked_shr(self, rhs) + } } /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if @@ -1743,10 +1914,10 @@ macro_rules! uint_impl { } /// Wrapping (modular) division. Computes `self / rhs`. - /// Wrapped division on unsigned types is just normal division. - /// There's no way wrapping could ever happen. - /// This function exists, so that all operations - /// are accounted for in the wrapping operations. + /// + /// Wrapped division on unsigned types is just normal division. There's + /// no way wrapping could ever happen. This function exists so that all + /// operations are accounted for in the wrapping operations. /// /// # Panics /// @@ -1770,13 +1941,12 @@ macro_rules! uint_impl { } /// Wrapping Euclidean division. Computes `self.div_euclid(rhs)`. - /// Wrapped division on unsigned types is just normal division. - /// There's no way wrapping could ever happen. - /// This function exists, so that all operations - /// are accounted for in the wrapping operations. - /// Since, for the positive integers, all common - /// definitions of division are equal, this - /// is exactly equal to `self.wrapping_div(rhs)`. + /// + /// Wrapped division on unsigned types is just normal division. There's + /// no way wrapping could ever happen. This function exists so that all + /// operations are accounted for in the wrapping operations. Since, for + /// the positive integers, all common definitions of division are equal, + /// this is exactly equal to `self.wrapping_div(rhs)`. /// /// # Panics /// @@ -1800,11 +1970,11 @@ macro_rules! uint_impl { } /// Wrapping (modular) remainder. Computes `self % rhs`. - /// Wrapped remainder calculation on unsigned types is - /// just the regular remainder calculation. - /// There's no way wrapping could ever happen. - /// This function exists, so that all operations - /// are accounted for in the wrapping operations. + /// + /// Wrapped remainder calculation on unsigned types is just the regular + /// remainder calculation. There's no way wrapping could ever happen. + /// This function exists so that all operations are accounted for in the + /// wrapping operations. /// /// # Panics /// @@ -1828,14 +1998,13 @@ macro_rules! uint_impl { } /// Wrapping Euclidean modulo. Computes `self.rem_euclid(rhs)`. - /// Wrapped modulo calculation on unsigned types is - /// just the regular remainder calculation. - /// There's no way wrapping could ever happen. - /// This function exists, so that all operations - /// are accounted for in the wrapping operations. - /// Since, for the positive integers, all common - /// definitions of division are equal, this - /// is exactly equal to `self.wrapping_rem(rhs)`. + /// + /// Wrapped modulo calculation on unsigned types is just the regular + /// remainder calculation. There's no way wrapping could ever happen. + /// This function exists so that all operations are accounted for in the + /// wrapping operations. Since, for the positive integers, all common + /// definitions of division are equal, this is exactly equal to + /// `self.wrapping_rem(rhs)`. /// /// # Panics /// @@ -1991,7 +2160,7 @@ macro_rules! uint_impl { acc.wrapping_mul(base) } - /// Calculates `self` + `rhs` + /// Calculates `self` + `rhs`. /// /// Returns a tuple of the addition along with a boolean indicating /// whether an arithmetic overflow would occur. If an overflow would @@ -2065,7 +2234,7 @@ macro_rules! uint_impl { (c, b || d) } - /// Calculates `self` + `rhs` with a signed `rhs` + /// Calculates `self` + `rhs` with a signed `rhs`. /// /// Returns a tuple of the addition along with a boolean indicating /// whether an arithmetic overflow would occur. If an overflow would @@ -2370,6 +2539,7 @@ macro_rules! uint_impl { /// ``` #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(4), (0x10, false));")] #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".overflowing_shl(132), (0x10, true));")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".overflowing_shl(", stringify!($BITS_MINUS_ONE), "), (0, false));")] /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] @@ -2507,37 +2677,10 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn isqrt(self) -> Self { - if self < 2 { - return self; + match NonZero::new(self) { + Some(x) => x.isqrt().get(), + None => 0, } - - // The algorithm is based on the one presented in - // - // which cites as source the following C code: - // . - - let mut op = self; - let mut res = 0; - let mut one = 1 << (self.ilog2() & !1); - - while one != 0 { - if op >= res + one { - op -= res + one; - res = (res >> 1) + one; - } else { - res >>= 1; - } - one >>= 2; - } - - // SAFETY: the result is positive and fits in an integer with half as many bits. - // Inform the optimizer about it. - unsafe { - hint::assert_unchecked(0 < res); - hint::assert_unchecked(res < 1 << (Self::BITS / 2)); - } - - res } /// Performs Euclidean division. @@ -2643,7 +2786,7 @@ macro_rules! uint_impl { pub const fn div_ceil(self, rhs: Self) -> Self { let d = self / rhs; let r = self % rhs; - if r > 0 && rhs > 0 { + if r > 0 { d + 1 } else { d @@ -2710,6 +2853,35 @@ macro_rules! uint_impl { } } + /// Returns `true` if `self` is an integer multiple of `rhs`, and false otherwise. + /// + /// This function is equivalent to `self % rhs == 0`, except that it will not panic + /// for `rhs == 0`. Instead, `0.is_multiple_of(0) == true`, and for any non-zero `n`, + /// `n.is_multiple_of(0) == false`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(unsigned_is_multiple_of)] + #[doc = concat!("assert!(6_", stringify!($SelfT), ".is_multiple_of(2));")] + #[doc = concat!("assert!(!5_", stringify!($SelfT), ".is_multiple_of(2));")] + /// + #[doc = concat!("assert!(0_", stringify!($SelfT), ".is_multiple_of(0));")] + #[doc = concat!("assert!(!6_", stringify!($SelfT), ".is_multiple_of(0));")] + /// ``` + #[unstable(feature = "unsigned_is_multiple_of", issue = "128101")] + #[must_use] + #[inline] + #[rustc_inherit_overflow_checks] + pub const fn is_multiple_of(self, rhs: Self) -> bool { + match rhs { + 0 => self == 0, + _ => self % rhs == 0, + } + } + /// Returns `true` if and only if `self == 2^k` for some `k`. /// /// # Examples @@ -2755,7 +2927,7 @@ macro_rules! uint_impl { /// /// When return value overflows (i.e., `self > (1 << (N-1))` for type /// `uN`), it panics in debug mode and the return value is wrapped to 0 in - /// release mode (the only situation in which method can return 0). + /// release mode (the only situation in which this method can return 0). /// /// # Examples /// @@ -2776,7 +2948,7 @@ macro_rules! uint_impl { self.one_less_than_next_power_of_two() + 1 } - /// Returns the smallest power of two greater than or equal to `n`. If + /// Returns the smallest power of two greater than or equal to `self`. If /// the next power of two is greater than the type's maximum value, /// `None` is returned, otherwise the power of two is wrapped in `Some`. /// @@ -2823,7 +2995,7 @@ macro_rules! uint_impl { self.one_less_than_next_power_of_two().wrapping_add(1) } - /// Return the memory representation of this integer as a byte array in + /// Returns the memory representation of this integer as a byte array in /// big-endian (network) byte order. /// #[doc = $to_xe_bytes_doc] @@ -2843,7 +3015,7 @@ macro_rules! uint_impl { self.to_be().to_ne_bytes() } - /// Return the memory representation of this integer as a byte array in + /// Returns the memory representation of this integer as a byte array in /// little-endian byte order. /// #[doc = $to_xe_bytes_doc] @@ -2863,7 +3035,7 @@ macro_rules! uint_impl { self.to_le().to_ne_bytes() } - /// Return the memory representation of this integer as a byte array in + /// Returns the memory representation of this integer as a byte array in /// native byte order. /// /// As the target platform's native endianness is used, portable code @@ -2901,7 +3073,7 @@ macro_rules! uint_impl { unsafe { mem::transmute(self) } } - /// Create a native endian integer value from its representation + /// Creates a native endian integer value from its representation /// as a byte array in big endian. /// #[doc = $from_xe_bytes_doc] @@ -2930,7 +3102,7 @@ macro_rules! uint_impl { Self::from_be(Self::from_ne_bytes(bytes)) } - /// Create a native endian integer value from its representation + /// Creates a native endian integer value from its representation /// as a byte array in little endian. /// #[doc = $from_xe_bytes_doc] @@ -2959,7 +3131,7 @@ macro_rules! uint_impl { Self::from_le(Self::from_ne_bytes(bytes)) } - /// Create a native endian integer value from its memory representation + /// Creates a native endian integer value from its memory representation /// as a byte array in native endianness. /// /// As the target platform's native endianness is used, portable code diff --git a/library/core/src/num/wrapping.rs b/library/core/src/num/wrapping.rs index 16f0b6d913dfb..1ac6d3161c2f9 100644 --- a/library/core/src/num/wrapping.rs +++ b/library/core/src/num/wrapping.rs @@ -1,10 +1,10 @@ //! Definitions of `Wrapping`. use crate::fmt; -use crate::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign}; -use crate::ops::{BitXor, BitXorAssign, Div, DivAssign}; -use crate::ops::{Mul, MulAssign, Neg, Not, Rem, RemAssign}; -use crate::ops::{Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign}; +use crate::ops::{ + Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, + Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, +}; /// Provides intentionally-wrapped arithmetic on `T`. /// diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index 5e77788d8ea36..133ae04f02618 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -73,7 +73,6 @@ append_const_msg )] #[doc(alias = "+")] -#[const_trait] pub trait Add { /// The resulting type after applying the `+` operator. #[stable(feature = "rust1", since = "1.0.0")] @@ -95,8 +94,7 @@ pub trait Add { macro_rules! add_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_ops", issue = "90080")] - impl const Add for $t { + impl Add for $t { type Output = $t; #[inline] diff --git a/library/core/src/ops/async_function.rs b/library/core/src/ops/async_function.rs index 18bcee5a1c7e0..37fac2b126fac 100644 --- a/library/core/src/ops/async_function.rs +++ b/library/core/src/ops/async_function.rs @@ -4,7 +4,7 @@ use crate::marker::Tuple; /// An async-aware version of the [`Fn`](crate::ops::Fn) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[unstable(feature = "async_fn_traits", issue = "none")] +#[unstable(feature = "async_closure", issue = "62290")] #[rustc_paren_sugar] #[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] @@ -18,7 +18,7 @@ pub trait AsyncFn: AsyncFnMut { /// An async-aware version of the [`FnMut`](crate::ops::FnMut) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[unstable(feature = "async_fn_traits", issue = "none")] +#[unstable(feature = "async_closure", issue = "62290")] #[rustc_paren_sugar] #[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] @@ -26,6 +26,7 @@ pub trait AsyncFn: AsyncFnMut { pub trait AsyncFnMut: AsyncFnOnce { /// Future returned by [`AsyncFnMut::async_call_mut`] and [`AsyncFn::async_call`]. #[unstable(feature = "async_fn_traits", issue = "none")] + #[lang = "call_ref_future"] type CallRefFuture<'a>: Future where Self: 'a; @@ -38,7 +39,7 @@ pub trait AsyncFnMut: AsyncFnOnce { /// An async-aware version of the [`FnOnce`](crate::ops::FnOnce) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[unstable(feature = "async_fn_traits", issue = "none")] +#[unstable(feature = "async_closure", issue = "62290")] #[rustc_paren_sugar] #[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] @@ -46,10 +47,12 @@ pub trait AsyncFnMut: AsyncFnOnce { pub trait AsyncFnOnce { /// Future returned by [`AsyncFnOnce::async_call_once`]. #[unstable(feature = "async_fn_traits", issue = "none")] + #[lang = "call_once_future"] type CallOnceFuture: Future; /// Output type of the called closure's future. #[unstable(feature = "async_fn_traits", issue = "none")] + #[lang = "async_fn_once_output"] type Output; /// Call the [`AsyncFnOnce`], returning a future which may move out of the called closure. @@ -143,6 +146,7 @@ mod internal_implementation_detail { // `for<'env> fn() -> (&'env T, ...)`. This allows us to represent the binder // of the closure's self-capture, and these upvar types will be instantiated with // the `'closure_env` region provided to the associated type. + #[lang = "async_fn_kind_upvars"] type Upvars<'closure_env, Inputs, Upvars, BorrowedUpvarsAsFnPtr>; } } diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index e10c438ef4300..a2709c66b06ad 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -238,7 +238,7 @@ impl ControlFlow { /// They have mediocre names and non-obvious semantics, so aren't /// currently on a path to potential stabilization. impl ControlFlow { - /// Create a `ControlFlow` from any type implementing `Try`. + /// Creates a `ControlFlow` from any type implementing `Try`. #[inline] pub(crate) fn from_try(r: R) -> Self { match R::branch(r) { @@ -247,7 +247,7 @@ impl ControlFlow { } } - /// Convert a `ControlFlow` into any type implementing `Try`; + /// Converts a `ControlFlow` into any type implementing `Try`. #[inline] pub(crate) fn into_try(self) -> R { match self { diff --git a/library/core/src/ops/coroutine.rs b/library/core/src/ops/coroutine.rs index 6a6c5db1ab115..13df888d24c5c 100644 --- a/library/core/src/ops/coroutine.rs +++ b/library/core/src/ops/coroutine.rs @@ -76,6 +76,7 @@ pub trait Coroutine { /// values which are allowed to be returned each time a coroutine yields. /// For example an iterator-as-a-coroutine would likely have this type as /// `T`, the type being iterated over. + #[lang = "coroutine_yield"] type Yield; /// The type of value this coroutine returns. @@ -84,6 +85,7 @@ pub trait Coroutine { /// `return` statement or implicitly as the last expression of a coroutine /// literal. For example futures would use this as `Result` as it /// represents a completed future. + #[lang = "coroutine_return"] type Return; /// Resumes the execution of this coroutine. diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 9849410d484ca..f0d2c761ef35b 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -282,7 +282,7 @@ impl DerefMut for &mut T { /// FIXME(deref_patterns): The precise semantics are undecided; the rough idea is that /// successive calls to `deref`/`deref_mut` without intermediate mutation should be /// idempotent, in the sense that they return the same value as far as pattern-matching -/// is concerned. Calls to `deref`/`deref_mut`` must leave the pointer itself likewise +/// is concerned. Calls to `deref`/`deref_mut` must leave the pointer itself likewise /// unchanged. #[unstable(feature = "deref_pure_trait", issue = "87121")] #[lang = "deref_pure"] diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs index 36ae581e3f723..c6083a121d107 100644 --- a/library/core/src/ops/drop.rs +++ b/library/core/src/ops/drop.rs @@ -171,12 +171,13 @@ /// still be live when `T` gets dropped. The exact details of this analysis are not yet /// stably guaranteed and **subject to change**. Currently, the analysis works as follows: /// - If `T` has no drop glue, then trivially nothing is required to be live. This is the case if -/// neither `T` nor any of its (recursive) fields have a destructor (`impl Drop`). [`PhantomData`] -/// and [`ManuallyDrop`] are considered to never have a destructor, no matter their field type. +/// neither `T` nor any of its (recursive) fields have a destructor (`impl Drop`). [`PhantomData`], +/// arrays of length 0 and [`ManuallyDrop`] are considered to never have a destructor, no matter +/// their field type. /// - If `T` has drop glue, then, for all types `U` that are *owned* by any field of `T`, /// recursively add the types and lifetimes that need to be live when `U` gets dropped. The set of /// owned types is determined by recursively traversing `T`: -/// - Recursively descend through `PhantomData`, `Box`, tuples, and arrays (including arrays of +/// - Recursively descend through `PhantomData`, `Box`, tuples, and arrays (excluding arrays of /// length 0). /// - Stop at reference and raw pointer types as well as function pointers and function items; /// they do not own anything. diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index 65bda9177c7be..64214eae377dd 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -1,4 +1,3 @@ -use crate::intrinsics::{unchecked_add, unchecked_sub}; use crate::iter::{FusedIterator, TrustedLen}; use crate::num::NonZero; use crate::ub_checks; @@ -46,7 +45,7 @@ impl IndexRange { #[inline] pub const fn len(&self) -> usize { // SAFETY: By invariant, this cannot wrap - unsafe { unchecked_sub(self.end, self.start) } + unsafe { self.end.unchecked_sub(self.start) } } /// # Safety @@ -57,7 +56,7 @@ impl IndexRange { let value = self.start; // SAFETY: The range isn't empty, so this cannot overflow - self.start = unsafe { unchecked_add(value, 1) }; + self.start = unsafe { value.unchecked_add(1) }; value } @@ -68,7 +67,7 @@ impl IndexRange { debug_assert!(self.start < self.end); // SAFETY: The range isn't empty, so this cannot overflow - let value = unsafe { unchecked_sub(self.end, 1) }; + let value = unsafe { self.end.unchecked_sub(1) }; self.end = value; value } @@ -83,7 +82,7 @@ impl IndexRange { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, // and thus the addition cannot overflow. - unsafe { unchecked_add(self.start, n) } + unsafe { self.start.unchecked_add(n) } } else { self.end }; @@ -102,7 +101,7 @@ impl IndexRange { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, // and thus the addition cannot overflow. - unsafe { unchecked_sub(self.end, n) } + unsafe { self.end.unchecked_sub(n) } } else { self.start }; diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 7bcfaadbe372b..98d41b71e8eb8 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -156,65 +156,44 @@ mod unsize; pub use self::arith::{Add, Div, Mul, Neg, Rem, Sub}; #[stable(feature = "op_assign_traits", since = "1.8.0")] pub use self::arith::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; - +#[unstable(feature = "async_fn_traits", issue = "none")] +pub use self::async_function::{AsyncFn, AsyncFnMut, AsyncFnOnce}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::bit::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; #[stable(feature = "op_assign_traits", since = "1.8.0")] pub use self::bit::{BitAndAssign, BitOrAssign, BitXorAssign, ShlAssign, ShrAssign}; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::deref::{Deref, DerefMut}; - +#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] +pub use self::control_flow::ControlFlow; +#[unstable(feature = "coroutine_trait", issue = "43122")] +pub use self::coroutine::{Coroutine, CoroutineState}; #[unstable(feature = "deref_pure_trait", issue = "87121")] pub use self::deref::DerefPure; - #[unstable(feature = "receiver_trait", issue = "none")] pub use self::deref::Receiver; - #[stable(feature = "rust1", since = "1.0.0")] -pub use self::drop::Drop; - +pub use self::deref::{Deref, DerefMut}; pub(crate) use self::drop::fallback_surface_drop; - +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::drop::Drop; #[stable(feature = "rust1", since = "1.0.0")] pub use self::function::{Fn, FnMut, FnOnce}; - -#[unstable(feature = "async_fn_traits", issue = "none")] -pub use self::async_function::{AsyncFn, AsyncFnMut, AsyncFnOnce}; - #[stable(feature = "rust1", since = "1.0.0")] pub use self::index::{Index, IndexMut}; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; - pub(crate) use self::index_range::IndexRange; - -#[stable(feature = "inclusive_range", since = "1.26.0")] -pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive}; - #[unstable(feature = "one_sided_range", issue = "69780")] pub use self::range::OneSidedRange; - -#[unstable(feature = "try_trait_v2", issue = "84277")] -pub use self::try_trait::{FromResidual, Try}; - -#[unstable(feature = "try_trait_v2_yeet", issue = "96374")] -pub use self::try_trait::Yeet; - +#[stable(feature = "inclusive_range", since = "1.26.0")] +pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; #[unstable(feature = "try_trait_v2_residual", issue = "91285")] pub use self::try_trait::Residual; - +#[unstable(feature = "try_trait_v2_yeet", issue = "96374")] +pub use self::try_trait::Yeet; pub(crate) use self::try_trait::{ChangeOutputType, NeverShortCircuit}; - -#[unstable(feature = "coroutine_trait", issue = "43122")] -pub use self::coroutine::{Coroutine, CoroutineState}; - +#[unstable(feature = "try_trait_v2", issue = "84277")] +pub use self::try_trait::{FromResidual, Try}; #[unstable(feature = "coerce_unsized", issue = "18598")] pub use self::unsize::CoerceUnsized; - #[unstable(feature = "dispatch_from_dyn", issue = "none")] pub use self::unsize::DispatchFromDyn; - -#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] -pub use self::control_flow::ControlFlow; diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index 483f55b207093..cd444c86ed06e 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -363,7 +363,9 @@ pub trait Residual { } #[unstable(feature = "pub_crate_should_not_need_unstable_attr", issue = "none")] -pub(crate) type ChangeOutputType = <::Residual as Residual>::TryType; +#[allow(type_alias_bounds)] +pub(crate) type ChangeOutputType>, V> = + >::TryType; /// An adapter for implementing non-try methods via the `Try` implementation. /// diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 1e3ed0f7c49f1..6c89c81018038 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -137,10 +137,13 @@ //! //! [^extern_fn]: this remains true for any argument/return types and any other ABI: `extern "abi" fn` (_e.g._, `extern "system" fn`) //! +//! Under some conditions the above types `T` are also null pointer optimized when wrapped in a [`Result`][result_repr]. +//! //! [`Box`]: ../../std/boxed/struct.Box.html //! [`num::NonZero*`]: crate::num //! [`ptr::NonNull`]: crate::ptr::NonNull //! [function call ABI]: ../primitive.fn.html#abi-compatibility +//! [result_repr]: crate::result#representation //! //! This is called the "null pointer optimization" or NPO. //! @@ -554,13 +557,10 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::iter::{self, FusedIterator, TrustedLen}; +use crate::ops::{self, ControlFlow, Deref, DerefMut}; use crate::panicking::{panic, panic_display}; use crate::pin::Pin; -use crate::{ - cmp, convert, hint, mem, - ops::{self, ControlFlow, Deref, DerefMut}, - slice, -}; +use crate::{cmp, convert, hint, mem, slice}; /// The `Option` type. See [the module level documentation](self) for more. #[derive(Copy, Eq, Debug, Hash)] @@ -651,6 +651,32 @@ impl Option { !self.is_some() } + /// Returns `true` if the option is a [`None`] or the value inside of it matches a predicate. + /// + /// # Examples + /// + /// ``` + /// #![feature(is_none_or)] + /// + /// let x: Option = Some(2); + /// assert_eq!(x.is_none_or(|x| x > 1), true); + /// + /// let x: Option = Some(0); + /// assert_eq!(x.is_none_or(|x| x > 1), false); + /// + /// let x: Option = None; + /// assert_eq!(x.is_none_or(|x| x > 1), true); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "is_none_or", issue = "126383")] + pub fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool { + match self { + None => true, + Some(x) => f(x), + } + } + ///////////////////////////////////////////////////////////////////////// // Adapter for working with references ///////////////////////////////////////////////////////////////////////// @@ -741,6 +767,13 @@ impl Option { } } + #[inline] + const fn len(&self) -> usize { + // Using the intrinsic avoids emitting a branch to get the 0 or 1. + let discriminant: isize = crate::intrinsics::discriminant_value(self); + discriminant as usize + } + /// Returns a slice of the contained value, if any. If this is `None`, an /// empty slice is returned. This can be useful to have a single type of /// iterator over an `Option` or slice. @@ -768,7 +801,8 @@ impl Option { #[inline] #[must_use] #[stable(feature = "option_as_slice", since = "1.75.0")] - pub fn as_slice(&self) -> &[T] { + #[rustc_const_unstable(feature = "const_option_ext", issue = "91930")] + pub const fn as_slice(&self) -> &[T] { // SAFETY: When the `Option` is `Some`, we're using the actual pointer // to the payload, with a length of 1, so this is equivalent to // `slice::from_ref`, and thus is safe. @@ -782,7 +816,7 @@ impl Option { unsafe { slice::from_raw_parts( (self as *const Self).byte_add(core::mem::offset_of!(Self, Some.0)).cast(), - usize::from(self.is_some()), + self.len(), ) } } @@ -822,7 +856,8 @@ impl Option { #[inline] #[must_use] #[stable(feature = "option_as_slice", since = "1.75.0")] - pub fn as_mut_slice(&mut self) -> &mut [T] { + #[rustc_const_unstable(feature = "const_option_ext", issue = "91930")] + pub const fn as_mut_slice(&mut self) -> &mut [T] { // SAFETY: When the `Option` is `Some`, we're using the actual pointer // to the payload, with a length of 1, so this is equivalent to // `slice::from_mut`, and thus is safe. @@ -838,7 +873,7 @@ impl Option { unsafe { slice::from_raw_parts_mut( (self as *mut Self).byte_add(core::mem::offset_of!(Self, Some.0)).cast(), - usize::from(self.is_some()), + self.len(), ) } } @@ -1705,8 +1740,6 @@ impl Option { /// # Examples /// /// ``` - /// #![feature(option_take_if)] - /// /// let mut x = Some(42); /// /// let prev = x.take_if(|v| if *v == 42 { @@ -1723,7 +1756,7 @@ impl Option { /// assert_eq!(prev, Some(43)); /// ``` #[inline] - #[unstable(feature = "option_take_if", issue = "98934")] + #[stable(feature = "option_take_if", since = "1.80.0")] pub fn take_if

(&mut self, predicate: P) -> Option where P: FnOnce(&mut T) -> bool, @@ -2213,10 +2246,8 @@ impl Iterator for Item { #[inline] fn size_hint(&self) -> (usize, Option) { - match self.opt { - Some(_) => (1, Some(1)), - None => (0, Some(0)), - } + let len = self.len(); + (len, Some(len)) } } @@ -2227,7 +2258,12 @@ impl DoubleEndedIterator for Item { } } -impl ExactSizeIterator for Item {} +impl ExactSizeIterator for Item { + #[inline] + fn len(&self) -> usize { + self.opt.len() + } +} impl FusedIterator for Item {} unsafe impl TrustedLen for Item {} @@ -2468,6 +2504,7 @@ impl ops::FromResidual for Option { } } +#[diagnostic::do_not_recommend] #[unstable(feature = "try_trait_v2_yeet", issue = "96374")] impl ops::FromResidual> for Option { #[inline] diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 8771f40f9b42b..6c5236ed99ce8 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -6,14 +6,15 @@ mod location; mod panic_info; mod unwind_safe; -use crate::any::Any; - #[stable(feature = "panic_hooks", since = "1.10.0")] pub use self::location::Location; #[stable(feature = "panic_hooks", since = "1.10.0")] pub use self::panic_info::PanicInfo; +#[stable(feature = "panic_info_message", since = "1.81.0")] +pub use self::panic_info::PanicMessage; #[stable(feature = "catch_unwind", since = "1.9.0")] pub use self::unwind_safe::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; +use crate::any::Any; #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] @@ -144,7 +145,7 @@ pub macro unreachable_2021 { /// use. #[unstable(feature = "std_internals", issue = "none")] #[doc(hidden)] -pub unsafe trait PanicPayload { +pub unsafe trait PanicPayload: crate::fmt::Display { /// Take full ownership of the contents. /// The return type is actually `Box`, but we cannot use `Box` in core. /// @@ -157,4 +158,9 @@ pub unsafe trait PanicPayload { /// Just borrow the contents. fn get(&mut self) -> &(dyn Any + Send); + + /// Tries to borrow the contents as `&str`, if possible without doing any allocations. + fn as_str(&mut self) -> Option<&str> { + None + } } diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index eb27da1724ec9..8c04994ac0fc4 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -2,9 +2,10 @@ use crate::fmt; /// A struct containing information about the location of a panic. /// -/// This structure is created by [`PanicInfo::location()`]. +/// This structure is created by [`PanicHookInfo::location()`] and [`PanicInfo::location()`]. /// /// [`PanicInfo::location()`]: crate::panic::PanicInfo::location +/// [`PanicHookInfo::location()`]: ../../std/panic/struct.PanicHookInfo.html#method.location /// /// # Examples /// diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index 403262212580c..e4d0c897b65ca 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -1,99 +1,65 @@ -use crate::any::Any; -use crate::fmt; +use crate::fmt::{self, Display}; use crate::panic::Location; /// A struct providing information about a panic. /// -/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`] -/// function. +/// A `PanicInfo` structure is passed to the panic handler defined by `#[panic_handler]`. /// -/// [`set_hook`]: ../../std/panic/fn.set_hook.html +/// For the type used by the panic hook mechanism in `std`, see [`std::panic::PanicHookInfo`]. /// -/// # Examples -/// -/// ```should_panic -/// use std::panic; -/// -/// panic::set_hook(Box::new(|panic_info| { -/// println!("panic occurred: {panic_info}"); -/// })); -/// -/// panic!("critical system failure"); -/// ``` +/// [`std::panic::PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html #[lang = "panic_info"] #[stable(feature = "panic_hooks", since = "1.10.0")] #[derive(Debug)] pub struct PanicInfo<'a> { - payload: &'a (dyn Any + Send), - message: Option<&'a fmt::Arguments<'a>>, + message: fmt::Arguments<'a>, location: &'a Location<'a>, can_unwind: bool, force_no_backtrace: bool, } +/// A message that was given to the `panic!()` macro. +/// +/// The [`Display`] implementation of this type will format the message with the arguments +/// that were given to the `panic!()` macro. +/// +/// See [`PanicInfo::message`]. +#[stable(feature = "panic_info_message", since = "1.81.0")] +pub struct PanicMessage<'a> { + message: fmt::Arguments<'a>, +} + impl<'a> PanicInfo<'a> { - #[unstable( - feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` and related macros", - issue = "none" - )] - #[doc(hidden)] #[inline] - pub fn internal_constructor( - message: Option<&'a fmt::Arguments<'a>>, + pub(crate) fn new( + message: fmt::Arguments<'a>, location: &'a Location<'a>, can_unwind: bool, force_no_backtrace: bool, ) -> Self { - struct NoPayload; - PanicInfo { location, message, payload: &NoPayload, can_unwind, force_no_backtrace } + PanicInfo { location, message, can_unwind, force_no_backtrace } } - #[unstable( - feature = "panic_internals", - reason = "internal details of the implementation of the `panic!` and related macros", - issue = "none" - )] - #[doc(hidden)] - #[inline] - pub fn set_payload(&mut self, info: &'a (dyn Any + Send)) { - self.payload = info; - } - - /// Returns the payload associated with the panic. - /// - /// This will commonly, but not always, be a `&'static str` or [`String`]. + /// The message that was given to the `panic!` macro. /// - /// [`String`]: ../../std/string/struct.String.html + /// # Example /// - /// # Examples - /// - /// ```should_panic - /// use std::panic; + /// The type returned by this method implements `Display`, so it can + /// be passed directly to [`write!()`] and similar macros. /// - /// panic::set_hook(Box::new(|panic_info| { - /// if let Some(s) = panic_info.payload().downcast_ref::<&str>() { - /// println!("panic occurred: {s:?}"); - /// } else { - /// println!("panic occurred"); - /// } - /// })); + /// [`write!()`]: core::write /// - /// panic!("Normal panic"); + /// ```ignore (no_std) + /// #[panic_handler] + /// fn panic_handler(panic_info: &PanicInfo<'_>) -> ! { + /// write!(DEBUG_OUTPUT, "panicked: {}", panic_info.message()); + /// loop {} + /// } /// ``` #[must_use] - #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn payload(&self) -> &(dyn Any + Send) { - self.payload - } - - /// If the `panic!` macro from the `core` crate (not from `std`) - /// was used with a formatting string and some additional arguments, - /// returns that message ready to be used for example with [`fmt::write`] - #[must_use] - #[unstable(feature = "panic_info_message", issue = "66745")] - pub fn message(&self) -> Option<&fmt::Arguments<'_>> { - self.message + #[stable(feature = "panic_info_message", since = "1.81.0")] + pub fn message(&self) -> PanicMessage<'_> { + PanicMessage { message: self.message } } /// Returns information about the location from which the panic originated, @@ -128,6 +94,24 @@ impl<'a> PanicInfo<'a> { Some(&self.location) } + /// Returns the payload associated with the panic. + /// + /// On this type, `core::panic::PanicInfo`, this method never returns anything useful. + /// It only exists because of compatibility with [`std::panic::PanicHookInfo`], + /// which used to be the same type. + /// + /// See [`std::panic::PanicHookInfo::payload`]. + /// + /// [`std::panic::PanicHookInfo`]: ../../std/panic/struct.PanicHookInfo.html + /// [`std::panic::PanicHookInfo::payload`]: ../../std/panic/struct.PanicHookInfo.html#method.payload + #[deprecated(since = "1.81.0", note = "this never returns anything useful")] + #[stable(feature = "panic_hooks", since = "1.10.0")] + #[allow(deprecated, deprecated_in_future)] + pub fn payload(&self) -> &(dyn crate::any::Any + Send) { + struct NoPayload; + &NoPayload + } + /// Returns whether the panic handler is allowed to unwind the stack from /// the point where the panic occurred. /// @@ -157,22 +141,50 @@ impl<'a> PanicInfo<'a> { } #[stable(feature = "panic_hook_display", since = "1.26.0")] -impl fmt::Display for PanicInfo<'_> { +impl Display for PanicInfo<'_> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("panicked at ")?; self.location.fmt(formatter)?; - formatter.write_str(":")?; - if let Some(message) = self.message { - formatter.write_str("\n")?; - formatter.write_fmt(*message)?; - } else if let Some(payload) = self.payload.downcast_ref::<&'static str>() { - formatter.write_str("\n")?; - formatter.write_str(payload)?; - } - // NOTE: we cannot use downcast_ref::() here - // since String is not available in core! - // The payload is a String when `std::panic!` is called with multiple arguments, - // but in that case the message is also available. + formatter.write_str(":\n")?; + formatter.write_fmt(self.message)?; Ok(()) } } + +impl<'a> PanicMessage<'a> { + /// Gets the formatted message, if it has no arguments to be formatted at runtime. + /// + /// This can be used to avoid allocations in some cases. + /// + /// # Guarantees + /// + /// For `panic!("just a literal")`, this function is guaranteed to + /// return `Some("just a literal")`. + /// + /// For most cases with placeholders, this function will return `None`. + /// + /// See [`fmt::Arguments::as_str`] for details. + #[stable(feature = "panic_info_message", since = "1.81.0")] + #[rustc_const_unstable(feature = "const_arguments_as_str", issue = "103900")] + #[must_use] + #[inline] + pub const fn as_str(&self) -> Option<&'static str> { + self.message.as_str() + } +} + +#[stable(feature = "panic_info_message", since = "1.81.0")] +impl Display for PanicMessage<'_> { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_fmt(self.message) + } +} + +#[stable(feature = "panic_info_message", since = "1.81.0")] +impl fmt::Debug for PanicMessage<'_> { + #[inline] + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_fmt(self.message) + } +} diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index ca06e059b75ac..7affe63825719 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -1,7 +1,14 @@ //! Panic support for core //! -//! The core library cannot define panicking, but it does *declare* panicking. This -//! means that the functions inside of core are allowed to panic, but to be +//! In core, panicking is always done with a message, resulting in a `core::panic::PanicInfo` +//! containing a `fmt::Arguments`. In std, however, panicking can be done with panic_any, which +//! throws a `Box` containing any type of value. Because of this, +//! `std::panic::PanicHookInfo` is a different type, which contains a `&dyn Any` instead of a +//! `fmt::Arguments`. std's panic handler will convert the `fmt::Arguments` to a `&dyn Any` +//! containing either a `&'static str` or `String` containing the formatted message. +//! +//! The core library cannot define any panic handler, but it can invoke it. +//! This means that the functions inside of core are allowed to panic, but to be //! useful an upstream crate must define panicking for core to use. The current //! interface for panicking is: //! @@ -10,11 +17,6 @@ //! # { loop {} } //! ``` //! -//! This definition allows for panicking with any general message, but it does not -//! allow for failing with a `Box` value. (`PanicInfo` just contains a `&(dyn Any + Send)`, -//! for which we fill in a dummy value in `PanicInfo::internal_constructor`.) -//! The reason for this is that core is not allowed to allocate. -//! //! This module contains a few other panicking functions, but these are just the //! necessary lang items for the compiler. All panics are funneled through this //! one function. The actual symbol is declared through the `#[panic_handler]` attribute. @@ -61,8 +63,8 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { fn panic_impl(pi: &PanicInfo<'_>) -> !; } - let pi = PanicInfo::internal_constructor( - Some(&fmt), + let pi = PanicInfo::new( + fmt, Location::caller(), /* can_unwind */ true, /* force_no_backtrace */ false, @@ -99,8 +101,8 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo } // PanicInfo with the `can_unwind` flag set to false forces an abort. - let pi = PanicInfo::internal_constructor( - Some(&fmt), + let pi = PanicInfo::new( + fmt, Location::caller(), /* can_unwind */ false, force_no_backtrace, @@ -292,10 +294,11 @@ fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { ) } -/// Panic because we cannot unwind out of a function. +/// Panics because we cannot unwind out of a function. /// /// This is a separate function to avoid the codesize impact of each crate containing the string to /// pass to `panic_nounwind`. +/// /// This function is called directly by the codegen backend, and must not have /// any extra arguments (including those synthesized by track_caller). #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] @@ -307,10 +310,11 @@ fn panic_cannot_unwind() -> ! { panic_nounwind("panic in a function that cannot unwind") } -/// Panic because we are unwinding out of a destructor during cleanup. +/// Panics because we are unwinding out of a destructor during cleanup. /// /// This is a separate function to avoid the codesize impact of each crate containing the string to /// pass to `panic_nounwind`. +/// /// This function is called directly by the codegen backend, and must not have /// any extra arguments (including those synthesized by track_caller). #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index d8fc3b7177f38..d752151d10cc8 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -184,7 +184,7 @@ //! requires at least a level of pointer indirection each time a new object is added to the mix //! (and, practically, a heap allocation). //! -//! Although there were other reason as well, this issue of expensive composition is the key thing +//! Although there were other reasons as well, this issue of expensive composition is the key thing //! that drove Rust towards adopting a different model. It is particularly a problem //! when one considers, for example, the implications of composing together the [`Future`]s which //! will eventually make up an asynchronous task (including address-sensitive `async fn` state @@ -421,7 +421,7 @@ //! } //! //! impl Unmovable { -//! /// Create a new `Unmovable`. +//! /// Creates a new `Unmovable`. //! /// //! /// To ensure the data doesn't move we place it on the heap behind a pinning Box. //! /// Note that the data is pinned, but the `Pin>` which is pinning it can @@ -920,11 +920,8 @@ #![stable(feature = "pin", since = "1.33.0")] -use crate::cmp; -use crate::fmt; use crate::hash::{Hash, Hasher}; use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, Receiver}; - #[allow(unused_imports)] use crate::{ cell::{RefCell, UnsafeCell}, @@ -932,6 +929,7 @@ use crate::{ marker::PhantomPinned, mem, ptr, }; +use crate::{cmp, fmt}; /// A pointer which pins its pointee in place. /// @@ -1168,7 +1166,7 @@ impl> Hash for Pin { } impl> Pin { - /// Construct a new `Pin` around a pointer to some data of a type that + /// Constructs a new `Pin` around a pointer to some data of a type that /// implements [`Unpin`]. /// /// Unlike `Pin::new_unchecked`, this method is safe because the pointer @@ -1223,7 +1221,7 @@ impl> Pin { } impl Pin { - /// Construct a new `Pin` around a reference to some data of a type that + /// Constructs a new `Pin` around a reference to some data of a type that /// may or may not implement [`Unpin`]. /// /// If `pointer` dereferences to an [`Unpin`] type, [`Pin::new`] should be used @@ -1569,7 +1567,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { self.__pointer } - /// Construct a new pin by mapping the interior value. + /// Constructs a new pin by mapping the interior value. /// /// For example, if you wanted to get a `Pin` of a field of something, /// you could use this to get access to that field in one line of code. @@ -1602,7 +1600,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { } impl Pin<&'static T> { - /// Get a pinning reference from a `&'static` reference. + /// Gets a pinning reference from a `&'static` reference. /// /// This is safe because `T` is borrowed immutably for the `'static` lifetime, which /// never ends. @@ -1639,8 +1637,8 @@ impl<'a, Ptr: DerefMut> Pin<&'a mut Pin> { // // We need to ensure that two things hold for that to be the case: // - // 1) Once we give out a `Pin<&mut Ptr::Target>`, an `&mut Ptr::Target` will not be given out. - // 2) By giving out a `Pin<&mut Ptr::Target>`, we do not risk of violating + // 1) Once we give out a `Pin<&mut Ptr::Target>`, a `&mut Ptr::Target` will not be given out. + // 2) By giving out a `Pin<&mut Ptr::Target>`, we do not risk violating // `Pin<&mut Pin>` // // The existence of `Pin` is sufficient to guarantee #1: since we already have a @@ -1656,7 +1654,7 @@ impl<'a, Ptr: DerefMut> Pin<&'a mut Pin> { } impl Pin<&'static mut T> { - /// Get a pinning mutable reference from a static mutable reference. + /// Gets a pinning mutable reference from a static mutable reference. /// /// This is safe because `T` is borrowed for the `'static` lifetime, which /// never ends. diff --git a/library/core/src/prelude/common.rs b/library/core/src/prelude/common.rs index afc6817aa1d24..e38ef1e147c76 100644 --- a/library/core/src/prelude/common.rs +++ b/library/core/src/prelude/common.rs @@ -2,6 +2,9 @@ //! //! See the [module-level documentation](super) for more. +// No formatting: this file is nothing but re-exports, and their order is worth preserving. +#![cfg_attr(rustfmt, rustfmt::skip)] + // Re-exported core operators #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] @@ -14,6 +17,9 @@ pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] pub use crate::mem::drop; +#[stable(feature = "size_of_prelude", since = "1.80.0")] +#[doc(no_inline)] +pub use crate::mem::{align_of, align_of_val, size_of, size_of_val}; // Re-exported types and traits #[stable(feature = "core_prelude", since = "1.4.0")] @@ -30,10 +36,7 @@ pub use crate::convert::{AsMut, AsRef, From, Into}; pub use crate::default::Default; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] -pub use crate::iter::{DoubleEndedIterator, ExactSizeIterator}; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::iter::{Extend, IntoIterator, Iterator}; +pub use crate::iter::{DoubleEndedIterator, ExactSizeIterator, Extend, IntoIterator, Iterator}; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] pub use crate::option::Option::{self, None, Some}; diff --git a/library/core/src/prelude/mod.rs b/library/core/src/prelude/mod.rs index ca33ef160e88b..496b78439ea6c 100644 --- a/library/core/src/prelude/mod.rs +++ b/library/core/src/prelude/mod.rs @@ -4,6 +4,9 @@ //! This module is imported by default when `#![no_std]` is used in the same //! manner as the standard library's prelude. +// No formatting: this file is nothing but re-exports, and their order is worth preserving. +#![cfg_attr(rustfmt, rustfmt::skip)] + #![stable(feature = "core_prelude", since = "1.4.0")] mod common; diff --git a/library/core/src/primitive.rs b/library/core/src/primitive.rs index e20b2c5c9382a..81a72118614dd 100644 --- a/library/core/src/primitive.rs +++ b/library/core/src/primitive.rs @@ -12,7 +12,7 @@ //! const SOME_PROPERTY: bool = true; //! } //! -//! # trait QueryId { const SOME_PROPERTY: core::primitive::bool; } +//! # trait QueryId { const SOME_PROPERTY: ::core::primitive::bool; } //! ``` //! //! Note that the `SOME_PROPERTY` associated constant would not compile, as its @@ -25,11 +25,17 @@ //! pub struct bool; //! //! impl QueryId for bool { -//! const SOME_PROPERTY: core::primitive::bool = true; +//! const SOME_PROPERTY: ::core::primitive::bool = true; //! } //! -//! # trait QueryId { const SOME_PROPERTY: core::primitive::bool; } +//! # trait QueryId { const SOME_PROPERTY: ::core::primitive::bool; } //! ``` +//! +//! We also used `::core` instead of `core`, because `core` can be +//! shadowed, too. Paths, starting with `::`, are searched in +//! the [extern prelude] since Edition 2018. +//! +//! [extern prelude]: https://doc.rust-lang.org/nightly/reference/names/preludes.html#extern-prelude #[stable(feature = "core_primitive", since = "1.43.0")] pub use bool; diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index e5f5bf6a50313..d51f2ef7edff3 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -1,4 +1,4 @@ -use safety::requires; +use safety::{ensures, requires}; use crate::num::NonZero; #[cfg(debug_assertions)] use crate::ub_checks::assert_unsafe_precondition; @@ -48,6 +48,8 @@ impl Alignment { #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] + #[requires(mem::align_of::().is_power_of_two())] + #[ensures(|result| result.as_usize().is_power_of_two())] pub const fn of() -> Self { // SAFETY: rustc ensures that type alignment is always a power of two. unsafe { Alignment::new_unchecked(mem::align_of::()) } @@ -60,6 +62,8 @@ impl Alignment { #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] + #[ensures(|result| align.is_power_of_two() == result.is_some())] + #[ensures(|result| result.is_none() || result.unwrap().as_usize() == align)] pub const fn new(align: usize) -> Option { if align.is_power_of_two() { // SAFETY: Just checked it only has one bit set @@ -80,8 +84,9 @@ impl Alignment { #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] - #[requires(align > 0)] - #[requires((align & (align - 1)) == 0)] + #[requires(align > 0 && (align & (align - 1)) == 0)] + #[ensures(|result| result.as_usize() == align)] + #[ensures(|result| result.as_usize().is_power_of_two())] pub const unsafe fn new_unchecked(align: usize) -> Self { #[cfg(debug_assertions)] assert_unsafe_precondition!( @@ -99,6 +104,7 @@ impl Alignment { #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] + #[ensures(|result| result.is_power_of_two())] pub const fn as_usize(self) -> usize { self.0 as usize } @@ -107,6 +113,8 @@ impl Alignment { #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] + #[ensures(|result| result.get().is_power_of_two())] + #[ensures(|result| result.get() == self.as_usize())] pub const fn as_nonzero(self) -> NonZero { // SAFETY: All the discriminants are non-zero. unsafe { NonZero::new_unchecked(self.as_usize()) } @@ -128,6 +136,9 @@ impl Alignment { #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] + #[requires(self.as_usize().is_power_of_two())] + #[ensures(|result| (*result as usize) < mem::size_of::() * 8)] + #[ensures(|result| 1usize << *result == self.as_usize())] pub const fn log2(self) -> u32 { self.as_nonzero().trailing_zeros() } @@ -158,6 +169,9 @@ impl Alignment { #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[rustc_const_unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] + #[ensures(|result| *result > 0)] + #[ensures(|result| *result == !(self.as_usize() -1))] + #[ensures(|result| self.as_usize() & *result == self.as_usize())] pub const fn mask(self) -> usize { // SAFETY: The alignment is always nonzero, and therefore decrementing won't overflow. !(unsafe { self.as_usize().unchecked_sub(1) }) @@ -370,3 +384,68 @@ enum AlignmentEnum { _Align1Shl62 = 1 << 62, _Align1Shl63 = 1 << 63, } + +#[cfg(kani)] +#[unstable(feature="kani", issue="none")] +mod verify { + use super::*; + + impl kani::Arbitrary for Alignment { + fn any() -> Self { + let align = kani::any_where(|a: &usize| a.is_power_of_two()); + unsafe { mem::transmute::(align) } + } + } + + // pub const fn of() -> Self + #[kani::proof_for_contract(Alignment::of)] + pub fn check_of_i32() { + let _ = Alignment::of::(); + } + + // pub const fn new(align: usize) -> Option + #[kani::proof_for_contract(Alignment::new)] + pub fn check_new() { + let a = kani::any::(); + let _ = Alignment::new(a); + } + + // pub const unsafe fn new_unchecked(align: usize) -> Self + #[kani::proof_for_contract(Alignment::new_unchecked)] + pub fn check_new_unchecked() { + let a = kani::any::(); + unsafe { + let _ = Alignment::new_unchecked(a); + } + } + + // pub const fn as_usize(self) -> usize + #[kani::proof_for_contract(Alignment::as_usize)] + pub fn check_as_usize() { + let a = kani::any::(); + if let Some(alignment) = Alignment::new(a) { + assert_eq!(alignment.as_usize(), a); + } + } + + // pub const fn as_nonzero(self) -> NonZero + #[kani::proof_for_contract(Alignment::as_nonzero)] + pub fn check_as_nonzero() { + let alignment = kani::any::(); + let _ = alignment.as_nonzero(); + } + + // pub const fn log2(self) -> u32 + #[kani::proof_for_contract(Alignment::log2)] + pub fn check_log2() { + let alignment = kani::any::(); + let _ = alignment.log2(); + } + + // pub const fn mask(self) -> usize + #[kani::proof_for_contract(Alignment::mask)] + pub fn check_mask() { + let alignment = kani::any::(); + let _ = alignment.mask(); + } +} diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index c8065b2e70906..93bbd92593f2c 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -61,7 +61,7 @@ impl *const T { self as _ } - /// Use the pointer value in a new pointer of another type. + /// Uses the pointer value in a new pointer of another type. /// /// In case `meta` is a (fat) pointer to an unsized type, this operation /// will ignore the pointer part, whereas for (thin) pointers to sized @@ -112,71 +112,6 @@ impl *const T { self as _ } - /// Casts a pointer to its raw bits. - /// - /// This is equivalent to `as usize`, but is more specific to enhance readability. - /// The inverse method is [`from_bits`](#method.from_bits). - /// - /// In particular, `*p as usize` and `p as usize` will both compile for - /// pointers to numeric types but do very different things, so using this - /// helps emphasize that reading the bits was intentional. - /// - /// # Examples - /// - /// ``` - /// #![feature(ptr_to_from_bits)] - /// # #[cfg(not(miri))] { // doctest does not work with strict provenance - /// let array = [13, 42]; - /// let p0: *const i32 = &array[0]; - /// assert_eq!(<*const _>::from_bits(p0.to_bits()), p0); - /// let p1: *const i32 = &array[1]; - /// assert_eq!(p1.to_bits() - p0.to_bits(), 4); - /// # } - /// ``` - #[unstable(feature = "ptr_to_from_bits", issue = "91126")] - #[deprecated( - since = "1.67.0", - note = "replaced by the `expose_provenance` method, or update your code \ - to follow the strict provenance rules using its APIs" - )] - #[inline(always)] - pub fn to_bits(self) -> usize - where - T: Sized, - { - self as usize - } - - /// Creates a pointer from its raw bits. - /// - /// This is equivalent to `as *const T`, but is more specific to enhance readability. - /// The inverse method is [`to_bits`](#method.to_bits). - /// - /// # Examples - /// - /// ``` - /// #![feature(ptr_to_from_bits)] - /// # #[cfg(not(miri))] { // doctest does not work with strict provenance - /// use std::ptr::NonNull; - /// let dangling: *const u8 = NonNull::dangling().as_ptr(); - /// assert_eq!(<*const u8>::from_bits(1), dangling); - /// # } - /// ``` - #[unstable(feature = "ptr_to_from_bits", issue = "91126")] - #[deprecated( - since = "1.67.0", - note = "replaced by the `ptr::with_exposed_provenance` function, or update \ - your code to follow the strict provenance rules using its APIs" - )] - #[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function - #[inline(always)] - pub fn from_bits(bits: usize) -> Self - where - T: Sized, - { - bits as Self - } - /// Gets the "address" portion of the pointer. /// /// This is similar to `self as usize`, which semantically discards *provenance* and @@ -330,7 +265,7 @@ impl *const T { /// /// unsafe { /// if let Some(val_back) = ptr.as_ref() { - /// println!("We got back the value: {val_back}!"); + /// assert_eq!(val_back, &10); /// } /// } /// ``` @@ -346,7 +281,7 @@ impl *const T { /// /// unsafe { /// let val_back = &*ptr; - /// println!("We got back the value: {val_back}!"); + /// assert_eq!(val_back, &10); /// } /// ``` #[stable(feature = "ptr_as_ref", since = "1.9.0")] @@ -393,7 +328,7 @@ impl *const T { /// let ptr: *const u8 = &10u8 as *const u8; /// /// unsafe { - /// println!("We got back the value: {}!", ptr.as_ref_unchecked()); + /// assert_eq!(ptr.as_ref_unchecked(), &10); /// } /// ``` // FIXME: mention it in the docs for `as_ref` and `as_uninit_ref` once stabilized. @@ -439,7 +374,7 @@ impl *const T { /// /// unsafe { /// if let Some(val_back) = ptr.as_uninit_ref() { - /// println!("We got back the value: {}!", val_back.assume_init()); + /// assert_eq!(val_back.assume_init(), 10); /// } /// } /// ``` @@ -455,37 +390,26 @@ impl *const T { if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit) }) } } - /// Calculates the offset from a pointer. + /// Adds an offset to a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting - /// pointer must be either in bounds or at the end of the same [allocated object]. - /// (If it is zero, then the function is always well-defined.) + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum, **in bytes** must fit in a usize. - /// - /// The compiler and standard library generally tries to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// Consider using [`wrapping_offset`] instead if these constraints are /// difficult to satisfy. The only advantage of this method is that it @@ -501,8 +425,8 @@ impl *const T { /// let ptr: *const u8 = s.as_ptr(); /// /// unsafe { - /// println!("{}", *ptr.offset(1) as char); - /// println!("{}", *ptr.offset(2) as char); + /// assert_eq!(*ptr.offset(1) as char, '2'); + /// assert_eq!(*ptr.offset(2) as char, '3'); /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -573,19 +497,21 @@ impl *const T { /// # Examples /// /// ``` + /// # use std::fmt::Write; /// // Iterate using a raw pointer in increments of two elements /// let data = [1u8, 2, 3, 4, 5]; /// let mut ptr: *const u8 = data.as_ptr(); /// let step = 2; /// let end_rounded_up = ptr.wrapping_offset(6); /// - /// // This loop prints "1, 3, 5, " + /// let mut out = String::new(); /// while ptr != end_rounded_up { /// unsafe { - /// print!("{}, ", *ptr); + /// write!(&mut out, "{}, ", *ptr).unwrap(); /// } /// ptr = ptr.wrapping_offset(step); /// } + /// assert_eq!(out.as_str(), "1, 3, 5, "); /// ``` #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] #[must_use = "returns a new pointer rather than modifying its argument"] @@ -674,38 +600,21 @@ impl *const T { /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// /// * `self` and `origin` must either /// + /// * point to the same address, or /// * both be *derived from* a pointer to the same [allocated object], and the memory range between - /// the two pointers must be either empty or in bounds of that object. (See below for an example.) - /// * or both be derived from an integer literal/constant, and point to the same address. + /// the two pointers must be in bounds of that object. (See below for an example.) /// /// * The distance between the pointers, in bytes, must be an exact multiple /// of the size of `T`. /// - /// * The distance between the pointers, **in bytes**, cannot overflow an `isize`. - /// - /// * The distance being in bounds cannot rely on "wrapping around" the address space. - /// - /// Rust types are never larger than `isize::MAX` and Rust allocations never wrap around the - /// address space, so two pointers within some value of any Rust type `T` will always satisfy - /// the last two conditions. The standard library also generally ensures that allocations - /// never reach a size where an offset is a concern. For instance, `Vec` and `Box` ensure they - /// never allocate more than `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())` - /// always satisfies the last two conditions. - /// - /// Most platforms fundamentally can't even construct such a large allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. - /// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on - /// such large allocations either.) + /// As a consequence, the absolute distance between the pointers, in bytes, computed on + /// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is + /// implied by the in-bounds requirement, and the fact that no allocated object can be larger + /// than `isize::MAX` bytes. /// /// The requirement for pointers to be derived from the same allocated object is primarily /// needed for `const`-compatibility: the distance between pointers into *different* allocated @@ -744,14 +653,14 @@ impl *const T { /// let ptr1 = Box::into_raw(Box::new(0u8)) as *const u8; /// let ptr2 = Box::into_raw(Box::new(1u8)) as *const u8; /// let diff = (ptr2 as isize).wrapping_sub(ptr1 as isize); - /// // Make ptr2_other an "alias" of ptr2, but derived from ptr1. - /// let ptr2_other = (ptr1 as *const u8).wrapping_offset(diff); + /// // Make ptr2_other an "alias" of ptr2.add(1), but derived from ptr1. + /// let ptr2_other = (ptr1 as *const u8).wrapping_offset(diff).wrapping_offset(1); /// assert_eq!(ptr2 as usize, ptr2_other as usize); /// // Since ptr2_other and ptr2 are derived from pointers to different objects, /// // computing their offset is undefined behavior, even though - /// // they point to the same address! + /// // they point to addresses that are in-bounds of the same object! /// unsafe { - /// let zero = ptr2_other.offset_from(ptr2); // Undefined Behavior + /// let one = ptr2_other.offset_from(ptr2); // Undefined Behavior! ⚠️ /// } /// ``` #[stable(feature = "ptr_offset_from", since = "1.47.0")] @@ -942,37 +851,26 @@ impl *const T { } } - /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`). + /// Adds an offset to a pointer (convenience for `.offset(count as isize)`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting - /// pointer must be either in bounds or at the end of the same [allocated object]. - /// (If it is zero, then the function is always well-defined.) + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a `usize`. - /// - /// The compiler and standard library generally tries to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// Consider using [`wrapping_add`] instead if these constraints are /// difficult to satisfy. The only advantage of this method is that it @@ -988,8 +886,8 @@ impl *const T { /// let ptr: *const u8 = s.as_ptr(); /// /// unsafe { - /// println!("{}", *ptr.add(1) as char); - /// println!("{}", *ptr.add(2) as char); + /// assert_eq!(*ptr.add(1), b'2'); + /// assert_eq!(*ptr.add(2), b'3'); /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] @@ -1026,7 +924,7 @@ impl *const T { unsafe { self.cast::().add(count).with_metadata_of(self) } } - /// Calculates the offset from a pointer (convenience for + /// Subtracts an offset from a pointer (convenience for /// `.offset((count as isize).wrapping_neg())`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer @@ -1034,30 +932,19 @@ impl *const T { /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: - /// - /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting - /// pointer must be either in bounds or at the end of the same [allocated object]. - /// (If it is zero, then the function is always well-defined.) - /// - /// * The computed offset cannot exceed `isize::MAX` **bytes**. + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a usize. + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// The compiler and standard library generally tries to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `vec.as_ptr().add(vec.len()).sub(vec.len())` is always safe. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// Consider using [`wrapping_sub`] instead if these constraints are /// difficult to satisfy. The only advantage of this method is that it @@ -1073,13 +960,14 @@ impl *const T { /// /// unsafe { /// let end: *const u8 = s.as_ptr().add(3); - /// println!("{}", *end.sub(1) as char); - /// println!("{}", *end.sub(2) as char); + /// assert_eq!(*end.sub(1), b'3'); + /// assert_eq!(*end.sub(2), b'2'); /// } /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] + #[rustc_allow_const_fn_unstable(unchecked_neg)] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn sub(self, count: usize) -> Self @@ -1093,7 +981,7 @@ impl *const T { // SAFETY: the caller must uphold the safety contract for `offset`. // Because the pointee is *not* a ZST, that means that `count` is // at most `isize::MAX`, and thus the negation cannot overflow. - unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) } + unsafe { self.offset((count as isize).unchecked_neg()) } } } @@ -1154,19 +1042,21 @@ impl *const T { /// # Examples /// /// ``` + /// # use std::fmt::Write; /// // Iterate using a raw pointer in increments of two elements /// let data = [1u8, 2, 3, 4, 5]; /// let mut ptr: *const u8 = data.as_ptr(); /// let step = 2; /// let end_rounded_up = ptr.wrapping_add(6); /// - /// // This loop prints "1, 3, 5, " + /// let mut out = String::new(); /// while ptr != end_rounded_up { /// unsafe { - /// print!("{}, ", *ptr); + /// write!(&mut out, "{}, ", *ptr).unwrap(); /// } /// ptr = ptr.wrapping_add(step); /// } + /// assert_eq!(out, "1, 3, 5, "); /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] @@ -1233,19 +1123,21 @@ impl *const T { /// # Examples /// /// ``` + /// # use std::fmt::Write; /// // Iterate using a raw pointer in increments of two elements (backwards) /// let data = [1u8, 2, 3, 4, 5]; /// let mut ptr: *const u8 = data.as_ptr(); /// let start_rounded_down = ptr.wrapping_sub(2); /// ptr = ptr.wrapping_add(4); /// let step = 2; - /// // This loop prints "5, 3, 1, " + /// let mut out = String::new(); /// while ptr != start_rounded_down { /// unsafe { - /// print!("{}, ", *ptr); + /// write!(&mut out, "{}, ", *ptr).unwrap(); /// } /// ptr = ptr.wrapping_sub(step); /// } + /// assert_eq!(out, "5, 3, 1, "); /// ``` #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index e501970b580de..ccc9f8754f00c 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -2,8 +2,9 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; -use crate::intrinsics::aggregate_raw_ptr; +use crate::intrinsics::{aggregate_raw_ptr, ptr_metadata}; use crate::marker::Freeze; +use crate::ptr::NonNull; /// Provides the pointer metadata type of any pointed-to type. /// @@ -79,7 +80,7 @@ pub trait Pointee { // NOTE: don’t stabilize this before trait aliases are stable in the language? pub trait Thin = Pointee; -/// Extract the metadata component of a pointer. +/// Extracts the metadata component of a pointer. /// /// Values of type `*mut T`, `&T`, or `&mut T` can be passed directly to this function /// as they implicitly coerce to `*const T`. @@ -94,10 +95,7 @@ pub trait Thin = Pointee; #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] pub const fn metadata(ptr: *const T) -> ::Metadata { - // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T - // and PtrComponents have the same memory layouts. Only std can make this - // guarantee. - unsafe { PtrRepr { const_ptr: ptr }.components.metadata } + ptr_metadata(ptr) } /// Forms a (possibly-wide) raw pointer from a data pointer and metadata. @@ -111,7 +109,7 @@ pub const fn metadata(ptr: *const T) -> ::Metadata { #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] pub const fn from_raw_parts( - data_pointer: *const (), + data_pointer: *const impl Thin, metadata: ::Metadata, ) -> *const T { aggregate_raw_ptr(data_pointer, metadata) @@ -125,35 +123,12 @@ pub const fn from_raw_parts( #[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[inline] pub const fn from_raw_parts_mut( - data_pointer: *mut (), + data_pointer: *mut impl Thin, metadata: ::Metadata, ) -> *mut T { aggregate_raw_ptr(data_pointer, metadata) } -#[repr(C)] -union PtrRepr { - const_ptr: *const T, - mut_ptr: *mut T, - components: PtrComponents, -} - -#[repr(C)] -struct PtrComponents { - data_pointer: *const (), - metadata: ::Metadata, -} - -// Manual impl needed to avoid `T: Copy` bound. -impl Copy for PtrComponents {} - -// Manual impl needed to avoid `T: Clone` bound. -impl Clone for PtrComponents { - fn clone(&self) -> Self { - *self - } -} - /// The metadata for a `Dyn = dyn SomeTrait` trait object type. /// /// It is a pointer to a vtable (virtual call table) @@ -178,7 +153,7 @@ impl Clone for PtrComponents { /// compare equal (since identical vtables can be deduplicated within a codegen unit). #[lang = "dyn_metadata"] pub struct DynMetadata { - _vtable_ptr: &'static VTable, + _vtable_ptr: NonNull, _phantom: crate::marker::PhantomData, } @@ -191,15 +166,18 @@ extern "C" { } impl DynMetadata { - /// One of the things that rustc_middle does with this being a lang item is - /// give it `FieldsShape::Primitive`, which means that as far as codegen can - /// tell, it *is* a reference, and thus doesn't have any fields. - /// That means we can't use field access, and have to transmute it instead. + /// When `DynMetadata` appears as the metadata field of a wide pointer, the rustc_middle layout + /// computation does magic and the resulting layout is *not* a `FieldsShape::Aggregate`, instead + /// it is a `FieldsShape::Primitive`. This means that the same type can have different layout + /// depending on whether it appears as the metadata field of a wide pointer or as a stand-alone + /// type, which understandably confuses codegen and leads to ICEs when trying to project to a + /// field of `DynMetadata`. To work around that issue, we use `transmute` instead of using a + /// field projection. #[inline] fn vtable_ptr(self) -> *const VTable { // SAFETY: this layout assumption is hard-coded into the compiler. // If it's somehow not a size match, the transmute will error. - unsafe { crate::mem::transmute::(self) } + unsafe { crate::mem::transmute::(self) } } /// Returns the size of the type associated with this vtable. @@ -209,18 +187,14 @@ impl DynMetadata { // Consider a reference like `&(i32, dyn Send)`: the vtable will only store the size of the // `Send` part! // SAFETY: DynMetadata always contains a valid vtable pointer - return unsafe { - crate::intrinsics::vtable_size(self.vtable_ptr() as *const ()) - }; + return unsafe { crate::intrinsics::vtable_size(self.vtable_ptr() as *const ()) }; } /// Returns the alignment of the type associated with this vtable. #[inline] pub fn align_of(self) -> usize { // SAFETY: DynMetadata always contains a valid vtable pointer - return unsafe { - crate::intrinsics::vtable_align(self.vtable_ptr() as *const ()) - }; + return unsafe { crate::intrinsics::vtable_align(self.vtable_ptr() as *const ()) }; } /// Returns the size and alignment together as a `Layout` diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 61d101c766f49..7fb8eae521fbf 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -196,9 +196,9 @@ //! * The **provenance** it has, defining the memory it has permission to access. //! Provenance can be absent, in which case the pointer does not have permission to access any memory. //! -//! Under Strict Provenance, a usize *cannot* accurately represent a pointer, and converting from -//! a pointer to a usize is generally an operation which *only* extracts the address. It is -//! therefore *impossible* to construct a valid pointer from a usize because there is no way +//! Under Strict Provenance, a `usize` *cannot* accurately represent a pointer, and converting from +//! a pointer to a `usize` is generally an operation which *only* extracts the address. It is +//! therefore *impossible* to construct a valid pointer from a `usize` because there is no way //! to restore the address-space and provenance. In other words, pointer-integer-pointer //! roundtrips are not possible (in the sense that the resulting pointer is not dereferenceable). //! @@ -234,16 +234,16 @@ //! //! Most code needs no changes to conform to strict provenance, as the only really concerning //! operation that *wasn't* obviously already Undefined Behaviour is casts from usize to a -//! pointer. For code which *does* cast a usize to a pointer, the scope of the change depends +//! pointer. For code which *does* cast a `usize` to a pointer, the scope of the change depends //! on exactly what you're doing. //! -//! In general you just need to make sure that if you want to convert a usize address to a +//! In general, you just need to make sure that if you want to convert a `usize` address to a //! pointer and then use that pointer to read/write memory, you need to keep around a pointer //! that has sufficient provenance to perform that read/write itself. In this way all of your //! casts from an address to a pointer are essentially just applying offsets/indexing. //! //! This is generally trivial to do for simple cases like tagged pointers *as long as you -//! represent the tagged pointer as an actual pointer and not a usize*. For instance: +//! represent the tagged pointer as an actual pointer and not a `usize`*. For instance: //! //! ``` //! #![feature(strict_provenance)] @@ -309,7 +309,7 @@ //! i.e. the usual "ZSTs are fake, do what you want" rules apply *but* this only applies //! for actual forgery (integers cast to pointers). If you borrow some struct's field //! that *happens* to be zero-sized, the resulting pointer will have provenance tied to -//! that allocation and it will still get invalidated if the allocation gets deallocated. +//! that allocation, and it will still get invalidated if the allocation gets deallocated. //! In the future we may introduce an API to make such a forged allocation explicit. //! //! * [`wrapping_offset`][] a pointer outside its provenance. This includes pointers @@ -409,13 +409,10 @@ #![allow(clippy::not_unsafe_ptr_arg_deref)] use crate::cmp::Ordering; -use crate::fmt; -use crate::hash; -use crate::intrinsics; use crate::marker::FnPtr; -use crate::ub_checks; +use crate::mem::{self, MaybeUninit}; +use crate::{fmt, hash, intrinsics, ub_checks}; -use crate::mem::{self, align_of, size_of, MaybeUninit}; #[cfg(kani)] use crate::kani; @@ -425,12 +422,10 @@ pub use alignment::Alignment; #[stable(feature = "rust1", since = "1.0.0")] #[doc(inline)] -pub use crate::intrinsics::copy_nonoverlapping; - +pub use crate::intrinsics::copy; #[stable(feature = "rust1", since = "1.0.0")] #[doc(inline)] -pub use crate::intrinsics::copy; - +pub use crate::intrinsics::copy_nonoverlapping; #[stable(feature = "rust1", since = "1.0.0")] #[doc(inline)] pub use crate::intrinsics::write_bytes; @@ -452,8 +447,13 @@ mod mut_ptr; /// Executes the destructor (if any) of the pointed-to value. /// -/// This is semantically equivalent to calling [`ptr::read`] and discarding +/// This is almost the same as calling [`ptr::read`] and discarding /// the result, but has the following advantages: +// FIXME: say something more useful than "almost the same"? +// There are open questions here: `read` requires the value to be fully valid, e.g. if `T` is a +// `bool` it must be 0 or 1, if it is a reference then it must be dereferenceable. `drop_in_place` +// only requires that `*to_drop` be "valid for dropping" and we have not defined what that means. In +// Miri it currently (May 2024) requires nothing at all for types without drop glue. /// /// * It is *required* to use `drop_in_place` to drop unsized types like /// trait objects, because they can't be read out onto the stack and @@ -567,7 +567,7 @@ pub unsafe fn drop_in_place(to_drop: *mut T) { #[rustc_allow_const_fn_unstable(ptr_metadata)] #[rustc_diagnostic_item = "ptr_null"] pub const fn null() -> *const T { - from_raw_parts(without_provenance(0), ()) + from_raw_parts(without_provenance::<()>(0), ()) } /// Creates a null mutable raw pointer. @@ -593,7 +593,7 @@ pub const fn null() -> *const T { #[rustc_allow_const_fn_unstable(ptr_metadata)] #[rustc_diagnostic_item = "ptr_null_mut"] pub const fn null_mut() -> *mut T { - from_raw_parts_mut(without_provenance_mut(0), ()) + from_raw_parts_mut(without_provenance_mut::<()>(0), ()) } /// Creates a pointer with the given address and no provenance. @@ -603,7 +603,7 @@ pub const fn null_mut() -> *mut T { /// Without provenance, this pointer is not associated with any actual allocation. Such a /// no-provenance pointer may be used for zero-sized memory accesses (if suitably aligned), but /// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers are -/// little more than a usize address in disguise. +/// little more than a `usize` address in disguise. /// /// This is different from `addr as *const T`, which creates a pointer that picks up a previously /// exposed provenance. See [`with_exposed_provenance`] for more details on that operation. @@ -647,7 +647,7 @@ pub const fn dangling() -> *const T { /// Without provenance, this pointer is not associated with any actual allocation. Such a /// no-provenance pointer may be used for zero-sized memory accesses (if suitably aligned), but /// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers are -/// little more than a usize address in disguise. +/// little more than a `usize` address in disguise. /// /// This is different from `addr as *mut T`, which creates a pointer that picks up a previously /// exposed provenance. See [`with_exposed_provenance_mut`] for more details on that operation. @@ -684,7 +684,7 @@ pub const fn dangling_mut() -> *mut T { without_provenance_mut(mem::align_of::()) } -/// Convert an address back to a pointer, picking up a previously 'exposed' provenance. +/// Converts an address back to a pointer, picking up a previously 'exposed' provenance. /// /// This is a more rigorously specified alternative to `addr as *const T`. The provenance of the /// returned pointer is that of *any* pointer that was previously exposed by passing it to @@ -695,7 +695,7 @@ pub const fn dangling_mut() -> *mut T { /// /// If there is no 'exposed' provenance that justifies the way this pointer will be used, /// the program has undefined behavior. In particular, the aliasing rules still apply: pointers -/// and references that have been invalidated due to aliasing accesses cannot be used any more, +/// and references that have been invalidated due to aliasing accesses cannot be used anymore, /// even if they have been exposed! /// /// Note that there is no algorithm that decides which provenance will be used. You can think of this @@ -732,7 +732,7 @@ where addr as *const T } -/// Convert an address back to a mutable pointer, picking up a previously 'exposed' provenance. +/// Converts an address back to a mutable pointer, picking up a previously 'exposed' provenance. /// /// This is a more rigorously specified alternative to `addr as *mut T`. The provenance of the /// returned pointer is that of *any* pointer that was previously passed to @@ -772,10 +772,53 @@ where addr as *mut T } -/// Convert a reference to a raw pointer. +/// Converts a reference to a raw pointer. +/// +/// For `r: &T`, `from_ref(r)` is equivalent to `r as *const T` (except for the caveat noted below), +/// but is a bit safer since it will never silently change type or mutability, in particular if the +/// code is refactored. /// -/// This is equivalent to `r as *const T`, but is a bit safer since it will never silently change -/// type or mutability, in particular if the code is refactored. +/// The caller must ensure that the pointee outlives the pointer this function returns, or else it +/// will end up dangling. +/// +/// The caller must also ensure that the memory the pointer (non-transitively) points to is never +/// written to (except inside an `UnsafeCell`) using this pointer or any pointer derived from it. If +/// you need to mutate the pointee, use [`from_mut`]. Specifically, to turn a mutable reference `m: +/// &mut T` into `*const T`, prefer `from_mut(m).cast_const()` to obtain a pointer that can later be +/// used for mutation. +/// +/// ## Interaction with lifetime extension +/// +/// Note that this has subtle interactions with the rules for lifetime extension of temporaries in +/// tail expressions. This code is valid, albeit in a non-obvious way: +/// ```rust +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// // The temporary holding the return value of `foo` has its lifetime extended, +/// // because the surrounding expression involves no function call. +/// let p = &foo() as *const T; +/// unsafe { p.read() }; +/// ``` +/// Naively replacing the cast with `from_ref` is not valid: +/// ```rust,no_run +/// # use std::ptr; +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// // The temporary holding the return value of `foo` does *not* have its lifetime extended, +/// // because the surrounding expression involves no function call. +/// let p = ptr::from_ref(&foo()); +/// unsafe { p.read() }; // UB! Reading from a dangling pointer ⚠️ +/// ``` +/// The recommended way to write this code is to avoid relying on lifetime extension +/// when raw pointers are involved: +/// ```rust +/// # use std::ptr; +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// let x = foo(); +/// let p = ptr::from_ref(&x); +/// unsafe { p.read() }; +/// ``` #[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] @@ -786,10 +829,47 @@ pub const fn from_ref(r: &T) -> *const T { r } -/// Convert a mutable reference to a raw pointer. +/// Converts a mutable reference to a raw pointer. +/// +/// For `r: &mut T`, `from_mut(r)` is equivalent to `r as *mut T` (except for the caveat noted +/// below), but is a bit safer since it will never silently change type or mutability, in particular +/// if the code is refactored. /// -/// This is equivalent to `r as *mut T`, but is a bit safer since it will never silently change -/// type or mutability, in particular if the code is refactored. +/// The caller must ensure that the pointee outlives the pointer this function returns, or else it +/// will end up dangling. +/// +/// ## Interaction with lifetime extension +/// +/// Note that this has subtle interactions with the rules for lifetime extension of temporaries in +/// tail expressions. This code is valid, albeit in a non-obvious way: +/// ```rust +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// // The temporary holding the return value of `foo` has its lifetime extended, +/// // because the surrounding expression involves no function call. +/// let p = &mut foo() as *mut T; +/// unsafe { p.write(T::default()) }; +/// ``` +/// Naively replacing the cast with `from_mut` is not valid: +/// ```rust,no_run +/// # use std::ptr; +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// // The temporary holding the return value of `foo` does *not* have its lifetime extended, +/// // because the surrounding expression involves no function call. +/// let p = ptr::from_mut(&mut foo()); +/// unsafe { p.write(T::default()) }; // UB! Writing to a dangling pointer ⚠️ +/// ``` +/// The recommended way to write this code is to avoid relying on lifetime extension +/// when raw pointers are involved: +/// ```rust +/// # use std::ptr; +/// # type T = i32; +/// # fn foo() -> T { 42 } +/// let mut x = foo(); +/// let p = ptr::from_mut(&mut x); +/// unsafe { p.write(T::default()) }; +/// ``` #[inline(always)] #[must_use] #[stable(feature = "ptr_from_ref", since = "1.76.0")] @@ -837,7 +917,7 @@ pub const fn from_mut(r: &mut T) -> *mut T { #[rustc_allow_const_fn_unstable(ptr_metadata)] #[rustc_diagnostic_item = "ptr_slice_from_raw_parts"] pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { - intrinsics::aggregate_raw_ptr(data, len) + from_raw_parts(data, len) } /// Forms a raw mutable slice from a pointer and a length. @@ -883,7 +963,7 @@ pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { #[rustc_const_unstable(feature = "const_slice_from_raw_parts_mut", issue = "67456")] #[rustc_diagnostic_item = "ptr_slice_from_raw_parts_mut"] pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { - intrinsics::aggregate_raw_ptr(data, len) + from_raw_parts_mut(data, len) } /// Swaps the values at two mutable locations of the same type, without @@ -1094,7 +1174,7 @@ const unsafe fn swap_nonoverlapping_simple_untyped(x: *mut T, y: *mut T, coun // If we end up here, it's because we're using a simple type -- like // a small power-of-two-sized thing -- or a special type with particularly // large alignment, particularly SIMD types. - // Thus we're fine just reading-and-writing it, as either it's small + // Thus, we're fine just reading-and-writing it, as either it's small // and that works well anyway or it's special and the type's author // presumably wanted things to be done in the larger chunk. @@ -1287,7 +1367,7 @@ pub const unsafe fn read(src: *const T) -> T { // provides enough information to know that this is a typed operation. // However, as of March 2023 the compiler was not capable of taking advantage - // of that information. Thus the implementation here switched to an intrinsic, + // of that information. Thus, the implementation here switched to an intrinsic, // which lowers to `_0 = *src` in MIR, to address a few issues: // // - Using `MaybeUninit::assume_init` after a `copy_nonoverlapping` was not @@ -1385,7 +1465,7 @@ pub const unsafe fn read(src: *const T) -> T { /// /// # Examples /// -/// Read a usize value from a byte buffer: +/// Read a `usize` value from a byte buffer: /// /// ``` /// use std::mem; @@ -1567,7 +1647,7 @@ pub const unsafe fn write(dst: *mut T, src: T) { /// As a result, using `&packed.unaligned as *const FieldType` causes immediate /// *undefined behavior* in your program. /// -/// Instead you must use the [`ptr::addr_of_mut!`](addr_of_mut) +/// Instead, you must use the [`ptr::addr_of_mut!`](addr_of_mut) /// macro to create the pointer. You may use that returned pointer together with /// this function. /// @@ -1596,7 +1676,7 @@ pub const unsafe fn write(dst: *mut T, src: T) { /// /// # Examples /// -/// Write a usize value to a byte buffer: +/// Write a `usize` value to a byte buffer: /// /// ``` /// use std::mem; @@ -1808,10 +1888,9 @@ pub(crate) const unsafe fn align_offset(p: *const T, a: usize) -> usiz // FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <= // 1, where the method versions of these operations are not inlined. use intrinsics::{ - assume, cttz_nonzero, exact_div, mul_with_overflow, unchecked_rem, unchecked_sub, - wrapping_add, wrapping_mul, wrapping_sub, + assume, cttz_nonzero, exact_div, mul_with_overflow, unchecked_rem, unchecked_shl, + unchecked_shr, unchecked_sub, wrapping_add, wrapping_mul, wrapping_sub, }; - use intrinsics::{unchecked_shl, unchecked_shr}; /// Calculate multiplicative modular inverse of `x` modulo `m`. /// @@ -1934,7 +2013,7 @@ pub(crate) const unsafe fn align_offset(p: *const T, a: usize) -> usiz let y = cttz_nonzero(a); if x < y { x } else { y } }; - // SAFETY: gcdpow has an upper-bound that’s at most the number of bits in a usize. + // SAFETY: gcdpow has an upper-bound that’s at most the number of bits in a `usize`. let gcd = unsafe { unchecked_shl(1usize, gcdpow) }; // SAFETY: gcd is always greater or equal to 1. if addr & unsafe { unchecked_sub(gcd, 1) } == 0 { @@ -2133,7 +2212,7 @@ impl fmt::Debug for F { } } -/// Create a `const` raw pointer to a place, without creating an intermediate reference. +/// Creates a `const` raw pointer to a place, without creating an intermediate reference. /// /// Creating a reference with `&`/`&mut` is only allowed if the pointer is properly aligned /// and points to initialized data. For cases where those requirements do not hold, @@ -2207,7 +2286,7 @@ pub macro addr_of($place:expr) { &raw const $place } -/// Create a `mut` raw pointer to a place, without creating an intermediate reference. +/// Creates a `mut` raw pointer to a place, without creating an intermediate reference. /// /// Creating a reference with `&`/`&mut` is only allowed if the pointer is properly aligned /// and points to initialized data. For cases where those requirements do not hold, @@ -2310,4 +2389,3 @@ mod verify { assert_eq!(val, copy); } } - diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index c53953400addd..bcf9b889182c7 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -60,7 +60,7 @@ impl *mut T { self as _ } - /// Use the pointer value in a new pointer of another type. + /// Uses the pointer value in a new pointer of another type. /// /// In case `meta` is a (fat) pointer to an unsized type, this operation /// will ignore the pointer part, whereas for (thin) pointers to sized @@ -117,72 +117,6 @@ impl *mut T { self as _ } - /// Casts a pointer to its raw bits. - /// - /// This is equivalent to `as usize`, but is more specific to enhance readability. - /// The inverse method is [`from_bits`](pointer#method.from_bits-1). - /// - /// In particular, `*p as usize` and `p as usize` will both compile for - /// pointers to numeric types but do very different things, so using this - /// helps emphasize that reading the bits was intentional. - /// - /// # Examples - /// - /// ``` - /// #![feature(ptr_to_from_bits)] - /// # #[cfg(not(miri))] { // doctest does not work with strict provenance - /// let mut array = [13, 42]; - /// let mut it = array.iter_mut(); - /// let p0: *mut i32 = it.next().unwrap(); - /// assert_eq!(<*mut _>::from_bits(p0.to_bits()), p0); - /// let p1: *mut i32 = it.next().unwrap(); - /// assert_eq!(p1.to_bits() - p0.to_bits(), 4); - /// } - /// ``` - #[unstable(feature = "ptr_to_from_bits", issue = "91126")] - #[deprecated( - since = "1.67.0", - note = "replaced by the `expose_provenance` method, or update your code \ - to follow the strict provenance rules using its APIs" - )] - #[inline(always)] - pub fn to_bits(self) -> usize - where - T: Sized, - { - self as usize - } - - /// Creates a pointer from its raw bits. - /// - /// This is equivalent to `as *mut T`, but is more specific to enhance readability. - /// The inverse method is [`to_bits`](pointer#method.to_bits-1). - /// - /// # Examples - /// - /// ``` - /// #![feature(ptr_to_from_bits)] - /// # #[cfg(not(miri))] { // doctest does not work with strict provenance - /// use std::ptr::NonNull; - /// let dangling: *mut u8 = NonNull::dangling().as_ptr(); - /// assert_eq!(<*mut u8>::from_bits(1), dangling); - /// } - /// ``` - #[unstable(feature = "ptr_to_from_bits", issue = "91126")] - #[deprecated( - since = "1.67.0", - note = "replaced by the `ptr::with_exposed_provenance_mut` function, or \ - update your code to follow the strict provenance rules using its APIs" - )] - #[allow(fuzzy_provenance_casts)] // this is an unstable and semi-deprecated cast function - #[inline(always)] - pub fn from_bits(bits: usize) -> Self - where - T: Sized, - { - bits as Self - } - /// Gets the "address" portion of the pointer. /// /// This is similar to `self as usize`, which semantically discards *provenance* and @@ -470,37 +404,26 @@ impl *mut T { if self.is_null() { None } else { Some(unsafe { &*(self as *const MaybeUninit) }) } } - /// Calculates the offset from a pointer. + /// Adds an offset to a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting - /// pointer must be either in bounds or at the end of the same [allocated object]. - /// (If it is zero, then the function is always well-defined.) + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum, **in bytes** must fit in a usize. - /// - /// The compiler and standard library generally tries to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// Consider using [`wrapping_offset`] instead if these constraints are /// difficult to satisfy. The only advantage of this method is that it @@ -902,38 +825,21 @@ impl *mut T { /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// /// * `self` and `origin` must either /// + /// * point to the same address, or /// * both be *derived from* a pointer to the same [allocated object], and the memory range between - /// the two pointers must be either empty or in bounds of that object. (See below for an example.) - /// * or both be derived from an integer literal/constant, and point to the same address. + /// the two pointers must be in bounds of that object. (See below for an example.) /// /// * The distance between the pointers, in bytes, must be an exact multiple /// of the size of `T`. /// - /// * The distance between the pointers, **in bytes**, cannot overflow an `isize`. - /// - /// * The distance being in bounds cannot rely on "wrapping around" the address space. - /// - /// Rust types are never larger than `isize::MAX` and Rust allocations never wrap around the - /// address space, so two pointers within some value of any Rust type `T` will always satisfy - /// the last two conditions. The standard library also generally ensures that allocations - /// never reach a size where an offset is a concern. For instance, `Vec` and `Box` ensure they - /// never allocate more than `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())` - /// always satisfies the last two conditions. - /// - /// Most platforms fundamentally can't even construct such a large allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. - /// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on - /// such large allocations either.) + /// As a consequence, the absolute distance between the pointers, in bytes, computed on + /// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is + /// implied by the in-bounds requirement, and the fact that no allocated object can be larger + /// than `isize::MAX` bytes. /// /// The requirement for pointers to be derived from the same allocated object is primarily /// needed for `const`-compatibility: the distance between pointers into *different* allocated @@ -972,14 +878,14 @@ impl *mut T { /// let ptr1 = Box::into_raw(Box::new(0u8)); /// let ptr2 = Box::into_raw(Box::new(1u8)); /// let diff = (ptr2 as isize).wrapping_sub(ptr1 as isize); - /// // Make ptr2_other an "alias" of ptr2, but derived from ptr1. - /// let ptr2_other = (ptr1 as *mut u8).wrapping_offset(diff); + /// // Make ptr2_other an "alias" of ptr2.add(1), but derived from ptr1. + /// let ptr2_other = (ptr1 as *mut u8).wrapping_offset(diff).wrapping_offset(1); /// assert_eq!(ptr2 as usize, ptr2_other as usize); /// // Since ptr2_other and ptr2 are derived from pointers to different objects, /// // computing their offset is undefined behavior, even though - /// // they point to the same address! + /// // they point to addresses that are in-bounds of the same object! /// unsafe { - /// let zero = ptr2_other.offset_from(ptr2); // Undefined Behavior + /// let one = ptr2_other.offset_from(ptr2); // Undefined Behavior! ⚠️ /// } /// ``` #[stable(feature = "ptr_offset_from", since = "1.47.0")] @@ -1086,37 +992,26 @@ impl *mut T { unsafe { (self as *const T).sub_ptr(origin) } } - /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`). + /// Adds an offset to a pointer (convenience for `.offset(count as isize)`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting - /// pointer must be either in bounds or at the end of the same [allocated object]. - /// (If it is zero, then the function is always well-defined.) + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a `usize`. - /// - /// The compiler and standard library generally tries to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// Consider using [`wrapping_add`] instead if these constraints are /// difficult to satisfy. The only advantage of this method is that it @@ -1170,7 +1065,7 @@ impl *mut T { unsafe { self.cast::().add(count).with_metadata_of(self) } } - /// Calculates the offset from a pointer (convenience for + /// Subtracts an offset from a pointer (convenience for /// `.offset((count as isize).wrapping_neg())`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer @@ -1178,30 +1073,19 @@ impl *mut T { /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: - /// - /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting - /// pointer must be either in bounds or at the end of the same [allocated object]. - /// (If it is zero, then the function is always well-defined.) - /// - /// * The computed offset cannot exceed `isize::MAX` **bytes**. + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a usize. + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// The compiler and standard library generally tries to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `vec.as_ptr().add(vec.len()).sub(vec.len())` is always safe. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// Consider using [`wrapping_sub`] instead if these constraints are /// difficult to satisfy. The only advantage of this method is that it @@ -1224,6 +1108,7 @@ impl *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] + #[rustc_allow_const_fn_unstable(unchecked_neg)] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn sub(self, count: usize) -> Self @@ -1237,7 +1122,7 @@ impl *mut T { // SAFETY: the caller must uphold the safety contract for `offset`. // Because the pointee is *not* a ZST, that means that `count` is // at most `isize::MAX`, and thus the negation cannot overflow. - unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) } + unsafe { self.offset((count as isize).unchecked_neg()) } } } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 617890cf083b1..4a716a7503964 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1,15 +1,12 @@ use crate::cmp::Ordering; -use crate::fmt; -use crate::hash; -use crate::intrinsics; use crate::marker::Unsize; use crate::mem::{MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::{CoerceUnsized, DispatchFromDyn}; -use crate::ptr; use crate::ptr::Unique; use crate::slice::{self, SliceIndex}; use crate::ub_checks::assert_unsafe_precondition; +use crate::{fmt, hash, intrinsics, ptr}; /// `*mut T` but non-zero and [covariant]. /// @@ -476,36 +473,26 @@ impl NonNull { unsafe { NonNull { pointer: self.as_ptr() as *mut U } } } - /// Calculates the offset from a pointer. + /// Adds an offset to a pointer. /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum, **in bytes** must fit in a usize. - /// - /// The compiler and standard library generally tries to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// [allocated object]: crate::ptr#allocated-object /// @@ -525,8 +512,8 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[must_use = "returns a new pointer rather than modifying its argument"] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn offset(self, count: isize) -> Self where T: Sized, @@ -551,8 +538,8 @@ impl NonNull { #[must_use] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn byte_offset(self, count: isize) -> Self { // SAFETY: the caller must uphold the safety contract for `offset` and `byte_offset` has // the same safety contract. @@ -562,36 +549,26 @@ impl NonNull { unsafe { NonNull { pointer: self.pointer.byte_offset(count) } } } - /// Calculates the offset from a pointer (convenience for `.offset(count as isize)`). + /// Adds an offset to a pointer (convenience for `.offset(count as isize)`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer /// offset of `3 * size_of::()` bytes. /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: - /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset, **in bytes**, cannot overflow an `isize`. + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a `usize`. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// The compiler and standard library generally tries to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `vec.as_ptr().add(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// [allocated object]: crate::ptr#allocated-object /// @@ -611,8 +588,8 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[must_use = "returns a new pointer rather than modifying its argument"] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn add(self, count: usize) -> Self where T: Sized, @@ -638,8 +615,8 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_allow_const_fn_unstable(set_ptr_value)] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn byte_add(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `add` and `byte_add` has the same // safety contract. @@ -649,7 +626,7 @@ impl NonNull { unsafe { NonNull { pointer: self.pointer.byte_add(count) } } } - /// Calculates the offset from a pointer (convenience for + /// Subtracts an offset from a pointer (convenience for /// `.offset((count as isize).wrapping_neg())`). /// /// `count` is in units of T; e.g., a `count` of 3 represents a pointer @@ -657,29 +634,19 @@ impl NonNull { /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: - /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * The computed offset cannot exceed `isize::MAX` **bytes**. + /// * The computed offset, `count * size_of::()` bytes, must not overflow `isize`. /// - /// * The offset being in bounds cannot rely on "wrapping around" the address - /// space. That is, the infinite-precision sum must fit in a usize. + /// * If the computed offset is non-zero, then `self` must be derived from a pointer to some + /// [allocated object], and the entire memory range between `self` and the result must be in + /// bounds of that allocated object. In particular, this range must not "wrap around" the edge + /// of the address space. /// - /// The compiler and standard library generally tries to ensure allocations - /// never reach a size where an offset is a concern. For instance, `Vec` - /// and `Box` ensure they never allocate more than `isize::MAX` bytes, so - /// `vec.as_ptr().add(vec.len()).sub(vec.len())` is always safe. - /// - /// Most platforms fundamentally can't even construct such an allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. + /// Allocated objects can never be larger than `isize::MAX` bytes, so if the computed offset + /// stays in bounds of the allocated object, it is guaranteed to satisfy the first requirement. + /// This implies, for instance, that `vec.as_ptr().add(vec.len())` (for `vec: Vec`) is always + /// safe. /// /// [allocated object]: crate::ptr#allocated-object /// @@ -699,8 +666,9 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[must_use = "returns a new pointer rather than modifying its argument"] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_allow_const_fn_unstable(unchecked_neg)] pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, @@ -712,7 +680,7 @@ impl NonNull { // SAFETY: the caller must uphold the safety contract for `offset`. // Because the pointee is *not* a ZST, that means that `count` is // at most `isize::MAX`, and thus the negation cannot overflow. - unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) } + unsafe { self.offset((count as isize).unchecked_neg()) } } } @@ -731,8 +699,8 @@ impl NonNull { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[rustc_allow_const_fn_unstable(set_ptr_value)] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn byte_sub(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `sub` and `byte_sub` has the same // safety contract. @@ -760,38 +728,21 @@ impl NonNull { /// /// # Safety /// - /// If any of the following conditions are violated, the result is Undefined - /// Behavior: + /// If any of the following conditions are violated, the result is Undefined Behavior: /// - /// * Both `self` and `origin` must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * `self` and `origin` must either /// - /// * Both pointers must be *derived from* a pointer to the same object. - /// (See below for an example.) + /// * point to the same address, or + /// * both be *derived from* a pointer to the same [allocated object], and the memory range between + /// the two pointers must be in bounds of that object. (See below for an example.) /// /// * The distance between the pointers, in bytes, must be an exact multiple /// of the size of `T`. /// - /// * The distance between the pointers, **in bytes**, cannot overflow an `isize`. - /// - /// * The distance being in bounds cannot rely on "wrapping around" the address space. - /// - /// Rust types are never larger than `isize::MAX` and Rust allocations never wrap around the - /// address space, so two pointers within some value of any Rust type `T` will always satisfy - /// the last two conditions. The standard library also generally ensures that allocations - /// never reach a size where an offset is a concern. For instance, `Vec` and `Box` ensure they - /// never allocate more than `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())` - /// always satisfies the last two conditions. - /// - /// Most platforms fundamentally can't even construct such a large allocation. - /// For instance, no known 64-bit platform can ever serve a request - /// for 263 bytes due to page-table limitations or splitting the address space. - /// However, some 32-bit and 16-bit platforms may successfully serve a request for - /// more than `isize::MAX` bytes with things like Physical Address - /// Extension. As such, memory acquired directly from allocators or memory - /// mapped files *may* be too large to handle with this function. - /// (Note that [`offset`] and [`add`] also have a similar limitation and hence cannot be used on - /// such large allocations either.) + /// As a consequence, the absolute distance between the pointers, in bytes, computed on + /// mathematical integers (without "wrapping around"), cannot overflow an `isize`. This is + /// implied by the in-bounds requirement, and the fact that no allocated object can be larger + /// than `isize::MAX` bytes. /// /// The requirement for pointers to be derived from the same allocated object is primarily /// needed for `const`-compatibility: the distance between pointers into *different* allocated @@ -835,19 +786,20 @@ impl NonNull { /// let ptr1 = NonNull::new(Box::into_raw(Box::new(0u8))).unwrap(); /// let ptr2 = NonNull::new(Box::into_raw(Box::new(1u8))).unwrap(); /// let diff = (ptr2.addr().get() as isize).wrapping_sub(ptr1.addr().get() as isize); - /// // Make ptr2_other an "alias" of ptr2, but derived from ptr1. - /// let ptr2_other = NonNull::new(ptr1.as_ptr().wrapping_byte_offset(diff)).unwrap(); + /// // Make ptr2_other an "alias" of ptr2.add(1), but derived from ptr1. + /// let diff_plus_1 = diff.wrapping_add(1); + /// let ptr2_other = NonNull::new(ptr1.as_ptr().wrapping_byte_offset(diff_plus_1)).unwrap(); /// assert_eq!(ptr2.addr(), ptr2_other.addr()); /// // Since ptr2_other and ptr2 are derived from pointers to different objects, /// // computing their offset is undefined behavior, even though - /// // they point to the same address! + /// // they point to addresses that are in-bounds of the same object! /// - /// let zero = unsafe { ptr2_other.offset_from(ptr2) }; // Undefined Behavior + /// let one = unsafe { ptr2_other.offset_from(ptr2) }; // Undefined Behavior! ⚠️ /// ``` #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn offset_from(self, origin: NonNull) -> isize where T: Sized, @@ -867,8 +819,8 @@ impl NonNull { /// ignoring the metadata. #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn byte_offset_from(self, origin: NonNull) -> isize { // SAFETY: the caller must uphold the safety contract for `byte_offset_from`. unsafe { self.pointer.byte_offset_from(origin.pointer) } @@ -957,8 +909,8 @@ impl NonNull { /// [`ptr::read`]: crate::ptr::read() #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn read(self) -> T where T: Sized, @@ -979,7 +931,7 @@ impl NonNull { /// [`ptr::read_volatile`]: crate::ptr::read_volatile() #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] pub unsafe fn read_volatile(self) -> T where T: Sized, @@ -998,8 +950,8 @@ impl NonNull { /// [`ptr::read_unaligned`]: crate::ptr::read_unaligned() #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] + #[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")] pub const unsafe fn read_unaligned(self) -> T where T: Sized, @@ -1018,7 +970,7 @@ impl NonNull { /// [`ptr::copy`]: crate::ptr::copy() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] pub const unsafe fn copy_to(self, dest: NonNull, count: usize) where @@ -1038,7 +990,7 @@ impl NonNull { /// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] pub const unsafe fn copy_to_nonoverlapping(self, dest: NonNull, count: usize) where @@ -1058,7 +1010,7 @@ impl NonNull { /// [`ptr::copy`]: crate::ptr::copy() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] pub const unsafe fn copy_from(self, src: NonNull, count: usize) where @@ -1078,7 +1030,7 @@ impl NonNull { /// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] pub const unsafe fn copy_from_nonoverlapping(self, src: NonNull, count: usize) where @@ -1094,7 +1046,7 @@ impl NonNull { /// /// [`ptr::drop_in_place`]: crate::ptr::drop_in_place() #[inline(always)] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] pub unsafe fn drop_in_place(self) { // SAFETY: the caller must uphold the safety contract for `drop_in_place`. unsafe { ptr::drop_in_place(self.as_ptr()) } @@ -1108,7 +1060,7 @@ impl NonNull { /// [`ptr::write`]: crate::ptr::write() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] pub const unsafe fn write(self, val: T) where @@ -1127,7 +1079,7 @@ impl NonNull { #[inline(always)] #[doc(alias = "memset")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] pub const unsafe fn write_bytes(self, val: u8, count: usize) where @@ -1149,7 +1101,7 @@ impl NonNull { /// [`ptr::write_volatile`]: crate::ptr::write_volatile() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] pub unsafe fn write_volatile(self, val: T) where T: Sized, @@ -1168,7 +1120,7 @@ impl NonNull { /// [`ptr::write_unaligned`]: crate::ptr::write_unaligned() #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")] pub const unsafe fn write_unaligned(self, val: T) where @@ -1185,7 +1137,7 @@ impl NonNull { /// /// [`ptr::replace`]: crate::ptr::replace() #[inline(always)] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] pub unsafe fn replace(self, src: T) -> T where T: Sized, @@ -1202,7 +1154,7 @@ impl NonNull { /// /// [`ptr::swap`]: crate::ptr::swap() #[inline(always)] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_swap", issue = "83163")] pub const unsafe fn swap(self, with: NonNull) where @@ -1254,7 +1206,7 @@ impl NonNull { /// ``` #[inline] #[must_use] - #[stable(feature = "non_null_convenience", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "non_null_convenience", since = "1.80.0")] #[rustc_const_unstable(feature = "const_align_offset", issue = "90962")] pub const fn align_offset(self, align: usize) -> usize where @@ -1709,6 +1661,8 @@ impl NonNull<[T]> { /// // Note that calling `memory.as_mut()` is not allowed here as the content may be uninitialized. /// # #[allow(unused_variables)] /// let slice: &mut [MaybeUninit] = unsafe { memory.as_uninit_slice_mut() }; + /// # // Prevent leaks for Miri. + /// # unsafe { Global.deallocate(memory.cast(), Layout::new::<[u8; 32]>()); } /// # Ok::<_, std::alloc::AllocError>(()) /// ``` #[inline] diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index b74d691e45427..11e16dbbc149c 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -1,8 +1,12 @@ +use safety::{ensures, requires}; use crate::fmt; use crate::marker::{PhantomData, Unsize}; use crate::ops::{CoerceUnsized, DispatchFromDyn}; use crate::ptr::NonNull; +#[cfg(kani)] +use crate::kani; + /// A wrapper around a raw non-null `*mut T` that indicates that the possessor /// of this wrapper owns the referent. Useful for building abstractions like /// `Box`, `Vec`, `String`, and `HashMap`. @@ -84,6 +88,8 @@ impl Unique { /// /// `ptr` must be non-null. #[inline] + #[requires(!ptr.is_null())] + #[ensures(|result| result.as_ptr() == ptr)] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { // SAFETY: the caller must guarantee that `ptr` is non-null. unsafe { Unique { pointer: NonNull::new_unchecked(ptr), _marker: PhantomData } } @@ -91,6 +97,8 @@ impl Unique { /// Creates a new `Unique` if `ptr` is non-null. #[inline] + #[ensures(|result| result.is_none() == ptr.is_null())] + #[ensures(|result| result.is_none() || result.unwrap().as_ptr() == ptr)] pub const fn new(ptr: *mut T) -> Option { if let Some(pointer) = NonNull::new(ptr) { Some(Unique { pointer, _marker: PhantomData }) @@ -102,6 +110,7 @@ impl Unique { /// Acquires the underlying `*mut` pointer. #[must_use = "`self` will be dropped if the result is not used"] #[inline] + #[ensures(|result| !result.is_null())] pub const fn as_ptr(self) -> *mut T { self.pointer.as_ptr() } @@ -109,6 +118,7 @@ impl Unique { /// Acquires the underlying `*mut` pointer. #[must_use = "`self` will be dropped if the result is not used"] #[inline] + #[ensures(|result| result.as_ptr() == self.pointer.as_ptr())] pub const fn as_non_null_ptr(self) -> NonNull { self.pointer } @@ -201,3 +211,82 @@ impl From> for Unique { Unique { pointer, _marker: PhantomData } } } + +#[cfg(kani)] +#[unstable(feature="kani", issue="none")] +mod verify { + use super::*; + + // pub const unsafe fn new_unchecked(ptr: *mut T) -> Self + #[kani::proof_for_contract(Unique::new_unchecked)] + pub fn check_new_unchecked() { + let mut x : i32 = kani::any(); + let xptr = &mut x; + unsafe { + let _ = Unique::new_unchecked(xptr as *mut i32); + } + } + + // pub const fn new(ptr: *mut T) -> Option + #[kani::proof_for_contract(Unique::new)] + pub fn check_new() { + let mut x : i32 = kani::any(); + let xptr = &mut x; + let _ = Unique::new(xptr as *mut i32); + } + + // pub const fn as_ptr(self) -> *mut T + #[kani::proof_for_contract(Unique::as_ptr)] + pub fn check_as_ptr() { + let mut x : i32 = kani::any(); + let xptr = &mut x; + unsafe { + let unique = Unique::new_unchecked(xptr as *mut i32); + assert_eq!(unique.as_ptr(), xptr as *mut i32); + } + } + + // pub const fn as_non_null_ptr(self) -> NonNull + #[kani::proof_for_contract(Unique::as_non_null_ptr)] + pub fn check_as_non_null_ptr() { + let mut x : i32 = kani::any(); + let xptr = &mut x; + unsafe { + let unique = Unique::new_unchecked(xptr as *mut i32); + let _ = unique.as_non_null_ptr(); + } + } + + // pub const unsafe fn as_ref(&self) -> &T + #[kani::proof] + pub fn check_as_ref() { + let mut x : i32 = kani::any(); + let xptr = &mut x; + unsafe { + let unique = Unique::new_unchecked(xptr as *mut i32); + assert_eq!(*unique.as_ref(), x); + } + } + + // pub const unsafe fn as_mut(&mut self) -> &mut T + #[kani::proof] + pub fn check_as_mut() { + let mut x : i32 = kani::any(); + let xptr = &mut x; + unsafe { + let mut unique = Unique::new_unchecked(xptr as *mut i32); + assert_eq!(*unique.as_mut(), x); + } + } + + // pub const fn cast(self) -> Unique + #[kani::proof_for_contract(Unique::cast)] + pub fn check_cast() { + let mut x : i32 = kani::any(); + let xptr = &mut x; + unsafe { + let unique = Unique::new_unchecked(xptr as *mut i32); + assert_eq!(*unique.cast::().as_ref(), x as u32); + } + } +} diff --git a/library/core/src/range.rs b/library/core/src/range.rs new file mode 100644 index 0000000000000..408972c267f1a --- /dev/null +++ b/library/core/src/range.rs @@ -0,0 +1,492 @@ +//! # Experimental replacement range types +//! +//! The types within this module are meant to replace the existing +//! `Range`, `RangeInclusive`, and `RangeFrom` types in a future edition. +//! +//! ``` +//! #![feature(new_range_api)] +//! use core::range::{Range, RangeFrom, RangeInclusive}; +//! +//! let arr = [0, 1, 2, 3, 4]; +//! assert_eq!(arr[ .. ], [0, 1, 2, 3, 4]); +//! assert_eq!(arr[ .. 3 ], [0, 1, 2 ]); +//! assert_eq!(arr[ ..=3 ], [0, 1, 2, 3 ]); +//! assert_eq!(arr[ RangeFrom::from(1.. )], [ 1, 2, 3, 4]); +//! assert_eq!(arr[ Range::from(1..3 )], [ 1, 2 ]); +//! assert_eq!(arr[RangeInclusive::from(1..=3)], [ 1, 2, 3 ]); +//! ``` + +use crate::fmt; +use crate::hash::Hash; + +mod iter; + +#[unstable(feature = "new_range_api", issue = "125687")] +pub mod legacy; + +#[doc(inline)] +pub use iter::{IterRange, IterRangeFrom, IterRangeInclusive}; +use Bound::{Excluded, Included, Unbounded}; + +#[doc(inline)] +pub use crate::iter::Step; +#[doc(inline)] +pub use crate::ops::{Bound, OneSidedRange, RangeBounds, RangeFull, RangeTo, RangeToInclusive}; + +/// A (half-open) range bounded inclusively below and exclusively above +/// (`start..end` in a future edition). +/// +/// The range `start..end` contains all values with `start <= x < end`. +/// It is empty if `start >= end`. +/// +/// # Examples +/// +/// ``` +/// #![feature(new_range_api)] +/// use core::range::Range; +/// +/// assert_eq!(Range::from(3..5), Range { start: 3, end: 5 }); +/// assert_eq!(3 + 4 + 5, Range::from(3..6).into_iter().sum()); +/// ``` +#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)] +#[unstable(feature = "new_range_api", issue = "125687")] +pub struct Range { + /// The lower bound of the range (inclusive). + #[unstable(feature = "new_range_api", issue = "125687")] + pub start: Idx, + /// The upper bound of the range (exclusive). + #[unstable(feature = "new_range_api", issue = "125687")] + pub end: Idx, +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl fmt::Debug for Range { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + self.start.fmt(fmt)?; + write!(fmt, "..")?; + self.end.fmt(fmt)?; + Ok(()) + } +} + +impl Range { + /// Creates an iterator over the elements within this range. + /// + /// Shorthand for `.clone().into_iter()` + /// + /// # Examples + /// + /// ``` + /// #![feature(new_range_api)] + /// use core::range::Range; + /// + /// let mut i = Range::from(3..9).iter().map(|n| n*n); + /// assert_eq!(i.next(), Some(9)); + /// assert_eq!(i.next(), Some(16)); + /// assert_eq!(i.next(), Some(25)); + /// ``` + #[unstable(feature = "new_range_api", issue = "125687")] + #[inline] + pub fn iter(&self) -> IterRange { + self.clone().into_iter() + } +} + +impl> Range { + /// Returns `true` if `item` is contained in the range. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_range_api)] + /// use core::range::Range; + /// + /// assert!(!Range::from(3..5).contains(&2)); + /// assert!( Range::from(3..5).contains(&3)); + /// assert!( Range::from(3..5).contains(&4)); + /// assert!(!Range::from(3..5).contains(&5)); + /// + /// assert!(!Range::from(3..3).contains(&3)); + /// assert!(!Range::from(3..2).contains(&3)); + /// + /// assert!( Range::from(0.0..1.0).contains(&0.5)); + /// assert!(!Range::from(0.0..1.0).contains(&f32::NAN)); + /// assert!(!Range::from(0.0..f32::NAN).contains(&0.5)); + /// assert!(!Range::from(f32::NAN..1.0).contains(&0.5)); + /// ``` + #[inline] + #[unstable(feature = "new_range_api", issue = "125687")] + pub fn contains(&self, item: &U) -> bool + where + Idx: PartialOrd, + U: ?Sized + PartialOrd, + { + >::contains(self, item) + } + + /// Returns `true` if the range contains no items. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_range_api)] + /// use core::range::Range; + /// + /// assert!(!Range::from(3..5).is_empty()); + /// assert!( Range::from(3..3).is_empty()); + /// assert!( Range::from(3..2).is_empty()); + /// ``` + /// + /// The range is empty if either side is incomparable: + /// + /// ``` + /// #![feature(new_range_api)] + /// use core::range::Range; + /// + /// assert!(!Range::from(3.0..5.0).is_empty()); + /// assert!( Range::from(3.0..f32::NAN).is_empty()); + /// assert!( Range::from(f32::NAN..5.0).is_empty()); + /// ``` + #[inline] + #[unstable(feature = "new_range_api", issue = "125687")] + pub fn is_empty(&self) -> bool { + !(self.start < self.end) + } +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl RangeBounds for Range { + fn start_bound(&self) -> Bound<&T> { + Included(&self.start) + } + fn end_bound(&self) -> Bound<&T> { + Excluded(&self.end) + } +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl RangeBounds for Range<&T> { + fn start_bound(&self) -> Bound<&T> { + Included(self.start) + } + fn end_bound(&self) -> Bound<&T> { + Excluded(self.end) + } +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl From> for legacy::Range { + #[inline] + fn from(value: Range) -> Self { + Self { start: value.start, end: value.end } + } +} +#[unstable(feature = "new_range_api", issue = "125687")] +impl From> for Range { + #[inline] + fn from(value: legacy::Range) -> Self { + Self { start: value.start, end: value.end } + } +} + +/// A range bounded inclusively below and above (`start..=end`). +/// +/// The `RangeInclusive` `start..=end` contains all values with `x >= start` +/// and `x <= end`. It is empty unless `start <= end`. +/// +/// # Examples +/// +/// The `start..=end` syntax is a `RangeInclusive`: +/// +/// ``` +/// #![feature(new_range_api)] +/// use core::range::RangeInclusive; +/// +/// assert_eq!(RangeInclusive::from(3..=5), RangeInclusive { start: 3, end: 5 }); +/// assert_eq!(3 + 4 + 5, RangeInclusive::from(3..=5).into_iter().sum()); +/// ``` +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[unstable(feature = "new_range_api", issue = "125687")] +pub struct RangeInclusive { + /// The lower bound of the range (inclusive). + #[unstable(feature = "new_range_api", issue = "125687")] + pub start: Idx, + /// The upper bound of the range (inclusive). + #[unstable(feature = "new_range_api", issue = "125687")] + pub end: Idx, +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl fmt::Debug for RangeInclusive { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + self.start.fmt(fmt)?; + write!(fmt, "..=")?; + self.end.fmt(fmt)?; + Ok(()) + } +} + +impl> RangeInclusive { + /// Returns `true` if `item` is contained in the range. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_range_api)] + /// use core::range::RangeInclusive; + /// + /// assert!(!RangeInclusive::from(3..=5).contains(&2)); + /// assert!( RangeInclusive::from(3..=5).contains(&3)); + /// assert!( RangeInclusive::from(3..=5).contains(&4)); + /// assert!( RangeInclusive::from(3..=5).contains(&5)); + /// assert!(!RangeInclusive::from(3..=5).contains(&6)); + /// + /// assert!( RangeInclusive::from(3..=3).contains(&3)); + /// assert!(!RangeInclusive::from(3..=2).contains(&3)); + /// + /// assert!( RangeInclusive::from(0.0..=1.0).contains(&1.0)); + /// assert!(!RangeInclusive::from(0.0..=1.0).contains(&f32::NAN)); + /// assert!(!RangeInclusive::from(0.0..=f32::NAN).contains(&0.0)); + /// assert!(!RangeInclusive::from(f32::NAN..=1.0).contains(&1.0)); + /// ``` + #[inline] + #[unstable(feature = "new_range_api", issue = "125687")] + pub fn contains(&self, item: &U) -> bool + where + Idx: PartialOrd, + U: ?Sized + PartialOrd, + { + >::contains(self, item) + } + + /// Returns `true` if the range contains no items. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_range_api)] + /// use core::range::RangeInclusive; + /// + /// assert!(!RangeInclusive::from(3..=5).is_empty()); + /// assert!(!RangeInclusive::from(3..=3).is_empty()); + /// assert!( RangeInclusive::from(3..=2).is_empty()); + /// ``` + /// + /// The range is empty if either side is incomparable: + /// + /// ``` + /// #![feature(new_range_api)] + /// use core::range::RangeInclusive; + /// + /// assert!(!RangeInclusive::from(3.0..=5.0).is_empty()); + /// assert!( RangeInclusive::from(3.0..=f32::NAN).is_empty()); + /// assert!( RangeInclusive::from(f32::NAN..=5.0).is_empty()); + /// ``` + #[unstable(feature = "new_range_api", issue = "125687")] + #[inline] + pub fn is_empty(&self) -> bool { + !(self.start <= self.end) + } +} + +impl RangeInclusive { + /// Creates an iterator over the elements within this range. + /// + /// Shorthand for `.clone().into_iter()` + /// + /// # Examples + /// + /// ``` + /// #![feature(new_range_api)] + /// use core::range::RangeInclusive; + /// + /// let mut i = RangeInclusive::from(3..=8).iter().map(|n| n*n); + /// assert_eq!(i.next(), Some(9)); + /// assert_eq!(i.next(), Some(16)); + /// assert_eq!(i.next(), Some(25)); + /// ``` + #[unstable(feature = "new_range_api", issue = "125687")] + #[inline] + pub fn iter(&self) -> IterRangeInclusive { + self.clone().into_iter() + } +} + +impl RangeInclusive { + /// Converts to an exclusive `Range` for `SliceIndex` implementations. + /// The caller is responsible for dealing with `end == usize::MAX`. + #[inline] + pub(crate) const fn into_slice_range(self) -> Range { + Range { start: self.start, end: self.end + 1 } + } +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl RangeBounds for RangeInclusive { + fn start_bound(&self) -> Bound<&T> { + Included(&self.start) + } + fn end_bound(&self) -> Bound<&T> { + Included(&self.end) + } +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl RangeBounds for RangeInclusive<&T> { + fn start_bound(&self) -> Bound<&T> { + Included(self.start) + } + fn end_bound(&self) -> Bound<&T> { + Included(self.end) + } +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl From> for legacy::RangeInclusive { + #[inline] + fn from(value: RangeInclusive) -> Self { + Self::new(value.start, value.end) + } +} +#[unstable(feature = "new_range_api", issue = "125687")] +impl From> for RangeInclusive { + #[inline] + fn from(value: legacy::RangeInclusive) -> Self { + assert!( + !value.exhausted, + "attempted to convert from an exhausted `legacy::RangeInclusive` (unspecified behavior)" + ); + + let (start, end) = value.into_inner(); + RangeInclusive { start, end } + } +} + +/// A range only bounded inclusively below (`start..`). +/// +/// The `RangeFrom` `start..` contains all values with `x >= start`. +/// +/// *Note*: Overflow in the [`Iterator`] implementation (when the contained +/// data type reaches its numerical limit) is allowed to panic, wrap, or +/// saturate. This behavior is defined by the implementation of the [`Step`] +/// trait. For primitive integers, this follows the normal rules, and respects +/// the overflow checks profile (panic in debug, wrap in release). Note also +/// that overflow happens earlier than you might assume: the overflow happens +/// in the call to `next` that yields the maximum value, as the range must be +/// set to a state to yield the next value. +/// +/// [`Step`]: crate::iter::Step +/// +/// # Examples +/// +/// The `start..` syntax is a `RangeFrom`: +/// +/// ``` +/// #![feature(new_range_api)] +/// use core::range::RangeFrom; +/// +/// assert_eq!(RangeFrom::from(2..), core::range::RangeFrom { start: 2 }); +/// assert_eq!(2 + 3 + 4, RangeFrom::from(2..).into_iter().take(3).sum()); +/// ``` +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[unstable(feature = "new_range_api", issue = "125687")] +pub struct RangeFrom { + /// The lower bound of the range (inclusive). + #[unstable(feature = "new_range_api", issue = "125687")] + pub start: Idx, +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl fmt::Debug for RangeFrom { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + self.start.fmt(fmt)?; + write!(fmt, "..")?; + Ok(()) + } +} + +impl RangeFrom { + /// Creates an iterator over the elements within this range. + /// + /// Shorthand for `.clone().into_iter()` + /// + /// # Examples + /// + /// ``` + /// #![feature(new_range_api)] + /// use core::range::RangeFrom; + /// + /// let mut i = RangeFrom::from(3..).iter().map(|n| n*n); + /// assert_eq!(i.next(), Some(9)); + /// assert_eq!(i.next(), Some(16)); + /// assert_eq!(i.next(), Some(25)); + /// ``` + #[unstable(feature = "new_range_api", issue = "125687")] + #[inline] + pub fn iter(&self) -> IterRangeFrom { + self.clone().into_iter() + } +} + +impl> RangeFrom { + /// Returns `true` if `item` is contained in the range. + /// + /// # Examples + /// + /// ``` + /// #![feature(new_range_api)] + /// use core::range::RangeFrom; + /// + /// assert!(!RangeFrom::from(3..).contains(&2)); + /// assert!( RangeFrom::from(3..).contains(&3)); + /// assert!( RangeFrom::from(3..).contains(&1_000_000_000)); + /// + /// assert!( RangeFrom::from(0.0..).contains(&0.5)); + /// assert!(!RangeFrom::from(0.0..).contains(&f32::NAN)); + /// assert!(!RangeFrom::from(f32::NAN..).contains(&0.5)); + /// ``` + #[inline] + #[unstable(feature = "new_range_api", issue = "125687")] + pub fn contains(&self, item: &U) -> bool + where + Idx: PartialOrd, + U: ?Sized + PartialOrd, + { + >::contains(self, item) + } +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl RangeBounds for RangeFrom { + fn start_bound(&self) -> Bound<&T> { + Included(&self.start) + } + fn end_bound(&self) -> Bound<&T> { + Unbounded + } +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl RangeBounds for RangeFrom<&T> { + fn start_bound(&self) -> Bound<&T> { + Included(self.start) + } + fn end_bound(&self) -> Bound<&T> { + Unbounded + } +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl From> for legacy::RangeFrom { + #[inline] + fn from(value: RangeFrom) -> Self { + Self { start: value.start } + } +} +#[unstable(feature = "new_range_api", issue = "125687")] +impl From> for RangeFrom { + #[inline] + fn from(value: legacy::RangeFrom) -> Self { + Self { start: value.start } + } +} diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs new file mode 100644 index 0000000000000..4935280df60bc --- /dev/null +++ b/library/core/src/range/iter.rs @@ -0,0 +1,339 @@ +use crate::iter::{ + FusedIterator, Step, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, TrustedStep, +}; +use crate::num::NonZero; +use crate::range::{legacy, Range, RangeFrom, RangeInclusive}; + +/// By-value [`Range`] iterator. +#[unstable(feature = "new_range_api", issue = "125687")] +#[derive(Debug, Clone)] +pub struct IterRange(legacy::Range); + +impl IterRange { + /// Returns the remainder of the range being iterated over. + pub fn remainder(self) -> Range { + Range { start: self.0.start, end: self.0.end } + } +} + +/// Safety: This macro must only be used on types that are `Copy` and result in ranges +/// which have an exact `size_hint()` where the upper bound must not be `None`. +macro_rules! unsafe_range_trusted_random_access_impl { + ($($t:ty)*) => ($( + #[doc(hidden)] + #[unstable(feature = "trusted_random_access", issue = "none")] + unsafe impl TrustedRandomAccess for IterRange<$t> {} + + #[doc(hidden)] + #[unstable(feature = "trusted_random_access", issue = "none")] + unsafe impl TrustedRandomAccessNoCoerce for IterRange<$t> { + const MAY_HAVE_SIDE_EFFECT: bool = false; + } + )*) +} + +unsafe_range_trusted_random_access_impl! { + usize u8 u16 + isize i8 i16 +} + +#[cfg(target_pointer_width = "32")] +unsafe_range_trusted_random_access_impl! { + u32 i32 +} + +#[cfg(target_pointer_width = "64")] +unsafe_range_trusted_random_access_impl! { + u32 i32 + u64 i64 +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl Iterator for IterRange { + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + #[inline] + fn count(self) -> usize { + self.0.count() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + self.0.nth(n) + } + + #[inline] + fn last(self) -> Option { + self.0.last() + } + + #[inline] + fn min(self) -> Option + where + A: Ord, + { + self.0.min() + } + + #[inline] + fn max(self) -> Option + where + A: Ord, + { + self.0.max() + } + + #[inline] + fn is_sorted(self) -> bool { + true + } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + self.0.advance_by(n) + } + + #[inline] + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item + where + Self: TrustedRandomAccessNoCoerce, + { + // SAFETY: The TrustedRandomAccess contract requires that callers only pass an index + // that is in bounds. + // Additionally Self: TrustedRandomAccess is only implemented for Copy types + // which means even repeated reads of the same index would be safe. + unsafe { Step::forward_unchecked(self.0.start.clone(), idx) } + } +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl DoubleEndedIterator for IterRange { + #[inline] + fn next_back(&mut self) -> Option { + self.0.next_back() + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + self.0.nth_back(n) + } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + self.0.advance_back_by(n) + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IterRange {} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl FusedIterator for IterRange {} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl IntoIterator for Range { + type Item = A; + type IntoIter = IterRange; + + fn into_iter(self) -> Self::IntoIter { + IterRange(self.into()) + } +} + +/// By-value [`RangeInclusive`] iterator. +#[unstable(feature = "new_range_api", issue = "125687")] +#[derive(Debug, Clone)] +pub struct IterRangeInclusive(legacy::RangeInclusive); + +impl IterRangeInclusive { + /// Returns the remainder of the range being iterated over. + /// + /// If the iterator is exhausted or empty, returns `None`. + pub fn remainder(self) -> Option> { + if self.0.is_empty() { + return None; + } + + Some(RangeInclusive { start: self.0.start, end: self.0.end }) + } +} + +#[unstable(feature = "trusted_random_access", issue = "none")] +impl Iterator for IterRangeInclusive { + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + #[inline] + fn count(self) -> usize { + self.0.count() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + self.0.nth(n) + } + + #[inline] + fn last(self) -> Option { + self.0.last() + } + + #[inline] + fn min(self) -> Option + where + A: Ord, + { + self.0.min() + } + + #[inline] + fn max(self) -> Option + where + A: Ord, + { + self.0.max() + } + + #[inline] + fn is_sorted(self) -> bool { + true + } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + self.0.advance_by(n) + } +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl DoubleEndedIterator for IterRangeInclusive { + #[inline] + fn next_back(&mut self) -> Option { + self.0.next_back() + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + self.0.nth_back(n) + } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + self.0.advance_back_by(n) + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IterRangeInclusive {} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl FusedIterator for IterRangeInclusive {} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl IntoIterator for RangeInclusive { + type Item = A; + type IntoIter = IterRangeInclusive; + + fn into_iter(self) -> Self::IntoIter { + IterRangeInclusive(self.into()) + } +} + +// These macros generate `ExactSizeIterator` impls for various range types. +// +// * `ExactSizeIterator::len` is required to always return an exact `usize`, +// so no range can be longer than `usize::MAX`. +// * For integer types in `Range<_>` this is the case for types narrower than or as wide as `usize`. +// For integer types in `RangeInclusive<_>` +// this is the case for types *strictly narrower* than `usize` +// since e.g. `(0..=u64::MAX).len()` would be `u64::MAX + 1`. +macro_rules! range_exact_iter_impl { + ($($t:ty)*) => ($( + #[unstable(feature = "new_range_api", issue = "125687")] + impl ExactSizeIterator for IterRange<$t> { } + )*) +} + +macro_rules! range_incl_exact_iter_impl { + ($($t:ty)*) => ($( + #[unstable(feature = "new_range_api", issue = "125687")] + impl ExactSizeIterator for IterRangeInclusive<$t> { } + )*) +} + +range_exact_iter_impl! { + usize u8 u16 + isize i8 i16 +} + +range_incl_exact_iter_impl! { + u8 + i8 +} + +/// By-value [`RangeFrom`] iterator. +#[unstable(feature = "new_range_api", issue = "125687")] +#[derive(Debug, Clone)] +pub struct IterRangeFrom(legacy::RangeFrom); + +impl IterRangeFrom { + /// Returns the remainder of the range being iterated over. + pub fn remainder(self) -> RangeFrom { + RangeFrom { start: self.0.start } + } +} + +#[unstable(feature = "trusted_random_access", issue = "none")] +impl Iterator for IterRangeFrom { + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + self.0.nth(n) + } +} + +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IterRangeFrom {} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl FusedIterator for IterRangeFrom {} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl IntoIterator for RangeFrom { + type Item = A; + type IntoIter = IterRangeFrom; + + fn into_iter(self) -> Self::IntoIter { + IterRangeFrom(self.into()) + } +} diff --git a/library/core/src/range/legacy.rs b/library/core/src/range/legacy.rs new file mode 100644 index 0000000000000..6723c4903f756 --- /dev/null +++ b/library/core/src/range/legacy.rs @@ -0,0 +1,10 @@ +//! # Legacy range types +//! +//! The types within this module will be replaced by the types +//! [`Range`], [`RangeInclusive`], and [`RangeFrom`] in the parent +//! module, [`core::range`]. +//! +//! The types here are equivalent to those in [`core::ops`]. + +#[doc(inline)] +pub use crate::ops::{Range, RangeFrom, RangeInclusive}; diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 4c6dc4bba4377..7f278296b7b88 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -228,6 +228,27 @@ //! [`Err(E)`]: Err //! [io::Error]: ../../std/io/struct.Error.html "io::Error" //! +//! # Representation +//! +//! In some cases, [`Result`] will gain the same size, alignment, and ABI +//! guarantees as [`Option`] has. One of either the `T` or `E` type must be a +//! type that qualifies for the `Option` [representation guarantees][opt-rep], +//! and the *other* type must meet all of the following conditions: +//! * Is a zero-sized type with alignment 1 (a "1-ZST"). +//! * Has no fields. +//! * Does not have the `#[non_exhaustive]` attribute. +//! +//! For example, `NonZeroI32` qualifies for the `Option` representation +//! guarantees, and `()` is a zero-sized type with alignment 1, no fields, and +//! it isn't `non_exhaustive`. This means that both `Result` and +//! `Result<(), NonZeroI32>` have the same size, alignment, and ABI guarantees +//! as `Option`. The only difference is the implied semantics: +//! * `Option` is "a non-zero i32 might be present" +//! * `Result` is "a non-zero i32 success result, if any" +//! * `Result<(), NonZeroI32>` is "a non-zero i32 error result, if any" +//! +//! [opt-rep]: ../option/index.html#representation "Option Representation" +//! //! # Method overview //! //! In addition to working with pattern matching, [`Result`] provides a @@ -1969,7 +1990,7 @@ impl> ops::FromResidual> for Res } } } - +#[diagnostic::do_not_recommend] #[unstable(feature = "try_trait_v2_yeet", issue = "96374")] impl> ops::FromResidual> for Result { #[inline] diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 19c91ba2eb988..d1ea52fab6b87 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -1,12 +1,10 @@ //! Operations on ASCII `[u8]`. -use crate::ascii; -use crate::fmt::{self, Write}; -use crate::iter; -use crate::mem; -use crate::ops; use core::ascii::EscapeDefault; +use crate::fmt::{self, Write}; +use crate::{ascii, iter, mem, ops}; + #[cfg(not(test))] impl [u8] { /// Checks if all bytes in this slice are within the ASCII range. @@ -108,7 +106,7 @@ impl [u8] { without modifying the original"] #[stable(feature = "inherent_ascii_escape", since = "1.60.0")] pub fn escape_ascii(&self) -> EscapeAscii<'_> { - EscapeAscii { inner: self.iter().flat_map(|byte| byte.escape_ascii()) } + EscapeAscii { inner: self.iter().flat_map(EscapeByte) } } /// Returns a byte slice with leading ASCII whitespace bytes removed. @@ -123,8 +121,8 @@ impl [u8] { /// assert_eq!(b" ".trim_ascii_start(), b""); /// assert_eq!(b"".trim_ascii_start(), b""); /// ``` - #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] #[inline] pub const fn trim_ascii_start(&self) -> &[u8] { let mut bytes = self; @@ -152,8 +150,8 @@ impl [u8] { /// assert_eq!(b" ".trim_ascii_end(), b""); /// assert_eq!(b"".trim_ascii_end(), b""); /// ``` - #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] #[inline] pub const fn trim_ascii_end(&self) -> &[u8] { let mut bytes = self; @@ -182,15 +180,20 @@ impl [u8] { /// assert_eq!(b" ".trim_ascii(), b""); /// assert_eq!(b"".trim_ascii(), b""); /// ``` - #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] #[inline] pub const fn trim_ascii(&self) -> &[u8] { self.trim_ascii_start().trim_ascii_end() } } -type EscapeByte = impl (Fn(&u8) -> ascii::EscapeDefault) + Copy; +impl_fn_for_zst! { + #[derive(Clone)] + struct EscapeByte impl Fn = |byte: &u8| -> ascii::EscapeDefault { + ascii::escape_default(*byte) + }; +} /// An iterator over the escaped version of a byte slice. /// diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 5bee4d551352e..d19d0eae16671 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -1,12 +1,10 @@ //! Comparison traits for `[T]`. +use super::{from_raw_parts, memchr}; use crate::cmp::{self, BytewiseEq, Ordering}; use crate::intrinsics::compare_bytes; use crate::mem; -use super::from_raw_parts; -use super::memchr; - #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq<[U]> for [T] where diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 8d7b6165510a8..de1492e82ce7d 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -1,10 +1,8 @@ //! Indexing implementations for `[T]`. use crate::intrinsics::const_eval_select; -use crate::intrinsics::unchecked_sub; -use crate::ops; -use crate::ptr; use crate::ub_checks::assert_unsafe_precondition; +use crate::{ops, range}; #[stable(feature = "rust1", since = "1.0.0")] impl ops::Index for [T] @@ -107,8 +105,50 @@ const fn slice_end_index_overflow_fail() -> ! { panic!("attempted to index slice up to maximum usize"); } +// The UbChecks are great for catching bugs in the unsafe methods, but including +// them in safe indexing is unnecessary and hurts inlining and debug runtime perf. +// Both the safe and unsafe public methods share these helpers, +// which use intrinsics directly to get *no* extra checks. + +#[inline(always)] +const unsafe fn get_noubcheck(ptr: *const [T], index: usize) -> *const T { + let ptr = ptr as *const T; + // SAFETY: The caller already checked these preconditions + unsafe { crate::intrinsics::offset(ptr, index) } +} + +#[inline(always)] +const unsafe fn get_mut_noubcheck(ptr: *mut [T], index: usize) -> *mut T { + let ptr = ptr as *mut T; + // SAFETY: The caller already checked these preconditions + unsafe { crate::intrinsics::offset(ptr, index) } +} + +#[inline(always)] +const unsafe fn get_offset_len_noubcheck( + ptr: *const [T], + offset: usize, + len: usize, +) -> *const [T] { + // SAFETY: The caller already checked these preconditions + let ptr = unsafe { get_noubcheck(ptr, offset) }; + crate::intrinsics::aggregate_raw_ptr(ptr, len) +} + +#[inline(always)] +const unsafe fn get_offset_len_mut_noubcheck( + ptr: *mut [T], + offset: usize, + len: usize, +) -> *mut [T] { + // SAFETY: The caller already checked these preconditions + let ptr = unsafe { get_mut_noubcheck(ptr, offset) }; + crate::intrinsics::aggregate_raw_ptr(ptr, len) +} + mod private_slice_index { - use super::ops; + use super::{ops, range}; + #[stable(feature = "slice_get_slice", since = "1.28.0")] pub trait Sealed {} @@ -129,6 +169,13 @@ mod private_slice_index { #[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")] impl Sealed for (ops::Bound, ops::Bound) {} + #[unstable(feature = "new_range_api", issue = "125687")] + impl Sealed for range::Range {} + #[unstable(feature = "new_range_api", issue = "125687")] + impl Sealed for range::RangeInclusive {} + #[unstable(feature = "new_range_api", issue = "125687")] + impl Sealed for range::RangeFrom {} + impl Sealed for ops::IndexRange {} } @@ -166,6 +213,7 @@ pub unsafe trait SliceIndex: private_slice_index::Sealed { /// Returns a pointer to the output at this location, without /// performing any bounds checking. + /// /// Calling this method with an out-of-bounds index or a dangling `slice` pointer /// is *[undefined behavior]* even if the resulting pointer is not used. /// @@ -175,6 +223,7 @@ pub unsafe trait SliceIndex: private_slice_index::Sealed { /// Returns a mutable pointer to the output at this location, without /// performing any bounds checking. + /// /// Calling this method with an out-of-bounds index or a dangling `slice` pointer /// is *[undefined behavior]* even if the resulting pointer is not used. /// @@ -204,13 +253,17 @@ unsafe impl SliceIndex<[T]> for usize { #[inline] fn get(self, slice: &[T]) -> Option<&T> { // SAFETY: `self` is checked to be in bounds. - if self < slice.len() { unsafe { Some(&*self.get_unchecked(slice)) } } else { None } + if self < slice.len() { unsafe { Some(&*get_noubcheck(slice, self)) } } else { None } } #[inline] fn get_mut(self, slice: &mut [T]) -> Option<&mut T> { - // SAFETY: `self` is checked to be in bounds. - if self < slice.len() { unsafe { Some(&mut *self.get_unchecked_mut(slice)) } } else { None } + if self < slice.len() { + // SAFETY: `self` is checked to be in bounds. + unsafe { Some(&mut *get_mut_noubcheck(slice, self)) } + } else { + None + } } #[inline] @@ -228,7 +281,7 @@ unsafe impl SliceIndex<[T]> for usize { // Use intrinsics::assume instead of hint::assert_unchecked so that we don't check the // precondition of this function twice. crate::intrinsics::assume(self < slice.len()); - slice.as_ptr().add(self) + get_noubcheck(slice, self) } } @@ -240,7 +293,7 @@ unsafe impl SliceIndex<[T]> for usize { (this: usize = self, len: usize = slice.len()) => this < len ); // SAFETY: see comments for `get_unchecked` above. - unsafe { slice.as_mut_ptr().add(self) } + unsafe { get_mut_noubcheck(slice, self) } } #[inline] @@ -266,7 +319,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { fn get(self, slice: &[T]) -> Option<&[T]> { if self.end() <= slice.len() { // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { Some(&*self.get_unchecked(slice)) } + unsafe { Some(&*get_offset_len_noubcheck(slice, self.start(), self.len())) } } else { None } @@ -276,7 +329,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { if self.end() <= slice.len() { // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { Some(&mut *self.get_unchecked_mut(slice)) } + unsafe { Some(&mut *get_offset_len_mut_noubcheck(slice, self.start(), self.len())) } } else { None } @@ -293,7 +346,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { // cannot be longer than `isize::MAX`. They also guarantee that // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, // so the call to `add` is safe. - unsafe { ptr::slice_from_raw_parts(slice.as_ptr().add(self.start()), self.len()) } + unsafe { get_offset_len_noubcheck(slice, self.start(), self.len()) } } #[inline] @@ -305,14 +358,14 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { ); // SAFETY: see comments for `get_unchecked` above. - unsafe { ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start()), self.len()) } + unsafe { get_offset_len_mut_noubcheck(slice, self.start(), self.len()) } } #[inline] fn index(self, slice: &[T]) -> &[T] { if self.end() <= slice.len() { // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { &*self.get_unchecked(slice) } + unsafe { &*get_offset_len_noubcheck(slice, self.start(), self.len()) } } else { slice_end_index_len_fail(self.end(), slice.len()) } @@ -322,7 +375,7 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { fn index_mut(self, slice: &mut [T]) -> &mut [T] { if self.end() <= slice.len() { // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { &mut *self.get_unchecked_mut(slice) } + unsafe { &mut *get_offset_len_mut_noubcheck(slice, self.start(), self.len()) } } else { slice_end_index_len_fail(self.end(), slice.len()) } @@ -339,21 +392,26 @@ unsafe impl SliceIndex<[T]> for ops::Range { #[inline] fn get(self, slice: &[T]) -> Option<&[T]> { - if self.start > self.end || self.end > slice.len() { - None - } else { + // Using checked_sub is a safe way to get `SubUnchecked` in MIR + if let Some(new_len) = usize::checked_sub(self.end, self.start) + && self.end <= slice.len() + { // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { Some(&*self.get_unchecked(slice)) } + unsafe { Some(&*get_offset_len_noubcheck(slice, self.start, new_len)) } + } else { + None } } #[inline] fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - if self.start > self.end || self.end > slice.len() { - None - } else { + if let Some(new_len) = usize::checked_sub(self.end, self.start) + && self.end <= slice.len() + { // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { Some(&mut *self.get_unchecked_mut(slice)) } + unsafe { Some(&mut *get_offset_len_mut_noubcheck(slice, self.start, new_len)) } + } else { + None } } @@ -374,8 +432,10 @@ unsafe impl SliceIndex<[T]> for ops::Range { // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, // so the call to `add` is safe and the length calculation cannot overflow. unsafe { - let new_len = unchecked_sub(self.end, self.start); - ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), new_len) + // Using the intrinsic avoids a superfluous UB check, + // since the one on this method already checked `end >= start`. + let new_len = crate::intrinsics::unchecked_sub(self.end, self.start); + get_offset_len_noubcheck(slice, self.start, new_len) } } @@ -392,31 +452,71 @@ unsafe impl SliceIndex<[T]> for ops::Range { ); // SAFETY: see comments for `get_unchecked` above. unsafe { - let new_len = unchecked_sub(self.end, self.start); - ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), new_len) + let new_len = crate::intrinsics::unchecked_sub(self.end, self.start); + get_offset_len_mut_noubcheck(slice, self.start, new_len) } } #[inline(always)] fn index(self, slice: &[T]) -> &[T] { - if self.start > self.end { - slice_index_order_fail(self.start, self.end); - } else if self.end > slice.len() { + // Using checked_sub is a safe way to get `SubUnchecked` in MIR + let Some(new_len) = usize::checked_sub(self.end, self.start) else { + slice_index_order_fail(self.start, self.end) + }; + if self.end > slice.len() { slice_end_index_len_fail(self.end, slice.len()); } // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { &*self.get_unchecked(slice) } + unsafe { &*get_offset_len_noubcheck(slice, self.start, new_len) } } #[inline] fn index_mut(self, slice: &mut [T]) -> &mut [T] { - if self.start > self.end { - slice_index_order_fail(self.start, self.end); - } else if self.end > slice.len() { + let Some(new_len) = usize::checked_sub(self.end, self.start) else { + slice_index_order_fail(self.start, self.end) + }; + if self.end > slice.len() { slice_end_index_len_fail(self.end, slice.len()); } // SAFETY: `self` is checked to be valid and in bounds above. - unsafe { &mut *self.get_unchecked_mut(slice) } + unsafe { &mut *get_offset_len_mut_noubcheck(slice, self.start, new_len) } + } +} + +#[unstable(feature = "new_range_api", issue = "125687")] +unsafe impl SliceIndex<[T]> for range::Range { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&[T]> { + ops::Range::from(self).get(slice) + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { + ops::Range::from(self).get_mut(slice) + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { ops::Range::from(self).get_unchecked(slice) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { ops::Range::from(self).get_unchecked_mut(slice) } + } + + #[inline(always)] + fn index(self, slice: &[T]) -> &[T] { + ops::Range::from(self).index(slice) + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut [T] { + ops::Range::from(self).index_mut(slice) } } @@ -506,6 +606,43 @@ unsafe impl SliceIndex<[T]> for ops::RangeFrom { } } +#[unstable(feature = "new_range_api", issue = "125687")] +unsafe impl SliceIndex<[T]> for range::RangeFrom { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&[T]> { + ops::RangeFrom::from(self).get(slice) + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { + ops::RangeFrom::from(self).get_mut(slice) + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { ops::RangeFrom::from(self).get_unchecked(slice) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { ops::RangeFrom::from(self).get_unchecked_mut(slice) } + } + + #[inline] + fn index(self, slice: &[T]) -> &[T] { + ops::RangeFrom::from(self).index(slice) + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut [T] { + ops::RangeFrom::from(self).index_mut(slice) + } +} + #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] unsafe impl SliceIndex<[T]> for ops::RangeFull { @@ -590,6 +727,43 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { } } +#[unstable(feature = "new_range_api", issue = "125687")] +unsafe impl SliceIndex<[T]> for range::RangeInclusive { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&[T]> { + ops::RangeInclusive::from(self).get(slice) + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { + ops::RangeInclusive::from(self).get_mut(slice) + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { ops::RangeInclusive::from(self).get_unchecked(slice) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { ops::RangeInclusive::from(self).get_unchecked_mut(slice) } + } + + #[inline] + fn index(self, slice: &[T]) -> &[T] { + ops::RangeInclusive::from(self).index(slice) + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut [T] { + ops::RangeInclusive::from(self).index_mut(slice) + } +} + /// The methods `index` and `index_mut` panic if the end of the range is out of bounds. #[stable(feature = "inclusive_range", since = "1.26.0")] #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] @@ -629,13 +803,13 @@ unsafe impl SliceIndex<[T]> for ops::RangeToInclusive { } } -/// Performs bounds-checking of a range. +/// Performs bounds checking of a range. /// /// This method is similar to [`Index::index`] for slices, but it returns a /// [`Range`] equivalent to `range`. You can use this method to turn any range /// into `start` and `end` values. /// -/// `bounds` is the range of the slice to use for bounds-checking. It should +/// `bounds` is the range of the slice to use for bounds checking. It should /// be a [`RangeTo`] range that ends at the length of the slice. /// /// The returned [`Range`] is safe to pass to [`slice::get_unchecked`] and @@ -725,9 +899,9 @@ where ops::Range { start, end } } -/// Performs bounds-checking of a range without panicking. +/// Performs bounds checking of a range without panicking. /// -/// This is a version of [`range`] that returns [`None`] instead of panicking. +/// This is a version of [`range()`] that returns [`None`] instead of panicking. /// /// # Examples /// @@ -778,7 +952,8 @@ where if start > end || end > len { None } else { Some(ops::Range { start, end }) } } -/// Convert pair of `ops::Bound`s into `ops::Range` without performing any bounds checking and (in debug) overflow checking +/// Converts a pair of `ops::Bound`s into `ops::Range` without performing any +/// bounds checking or (in debug) overflow checking. pub(crate) fn into_range_unchecked( len: usize, (start, end): (ops::Bound, ops::Bound), @@ -797,7 +972,7 @@ pub(crate) fn into_range_unchecked( start..end } -/// Convert pair of `ops::Bound`s into `ops::Range`. +/// Converts pair of `ops::Bound`s into `ops::Range`. /// Returns `None` on overflowing indices. pub(crate) fn into_range( len: usize, @@ -822,7 +997,7 @@ pub(crate) fn into_range( Some(start..end) } -/// Convert pair of `ops::Bound`s into `ops::Range`. +/// Converts pair of `ops::Bound`s into `ops::Range`. /// Panics on overflowing indices. pub(crate) fn into_slice_range( len: usize, diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 96fc87ab2e9ec..62b170a87d445 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -3,8 +3,7 @@ #[macro_use] // import iterator! and forward_iterator! mod macros; -use crate::cmp; -use crate::fmt; +use super::{from_raw_parts, from_raw_parts_mut}; use crate::hint::assert_unchecked; use crate::iter::{ FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, UncheckedIterator, @@ -13,10 +12,9 @@ use crate::marker::PhantomData; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; use crate::ptr::{self, without_provenance, without_provenance_mut, NonNull}; +use crate::{cmp, fmt}; -use super::{from_raw_parts, from_raw_parts_mut}; - -#[stable(feature = "boxed_slice_into_iter", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] impl !Iterator for [T] {} #[stable(feature = "rust1", since = "1.0.0")] @@ -388,6 +386,9 @@ pub(super) trait SplitIter: DoubleEndedIterator { /// ``` /// let slice = [10, 40, 33, 20]; /// let mut iter = slice.split(|num| num % 3 == 0); +/// assert_eq!(iter.next(), Some(&[10, 40][..])); +/// assert_eq!(iter.next(), Some(&[20][..])); +/// assert_eq!(iter.next(), None); /// ``` /// /// [`split`]: slice::split @@ -541,6 +542,9 @@ impl FusedIterator for Split<'_, T, P> where P: FnMut(&T) -> bool {} /// ``` /// let slice = [10, 40, 33, 20]; /// let mut iter = slice.split_inclusive(|num| num % 3 == 0); +/// assert_eq!(iter.next(), Some(&[10, 40, 33][..])); +/// assert_eq!(iter.next(), Some(&[20][..])); +/// assert_eq!(iter.next(), None); /// ``` /// /// [`split_inclusive`]: slice::split_inclusive @@ -914,7 +918,10 @@ impl FusedIterator for SplitInclusiveMut<'_, T, P> where P: FnMut(&T) -> b /// /// ``` /// let slice = [11, 22, 33, 0, 44, 55]; -/// let iter = slice.rsplit(|num| *num == 0); +/// let mut iter = slice.rsplit(|num| *num == 0); +/// assert_eq!(iter.next(), Some(&[44, 55][..])); +/// assert_eq!(iter.next(), Some(&[11, 22, 33][..])); +/// assert_eq!(iter.next(), None); /// ``` /// /// [`rsplit`]: slice::rsplit @@ -1134,7 +1141,10 @@ impl> Iterator for GenericSplitN { /// /// ``` /// let slice = [10, 40, 30, 20, 60, 50]; -/// let iter = slice.splitn(2, |num| *num % 3 == 0); +/// let mut iter = slice.splitn(2, |num| *num % 3 == 0); +/// assert_eq!(iter.next(), Some(&[10, 40][..])); +/// assert_eq!(iter.next(), Some(&[20, 60, 50][..])); +/// assert_eq!(iter.next(), None); /// ``` /// /// [`splitn`]: slice::splitn @@ -1175,7 +1185,10 @@ where /// /// ``` /// let slice = [10, 40, 30, 20, 60, 50]; -/// let iter = slice.rsplitn(2, |num| *num % 3 == 0); +/// let mut iter = slice.rsplitn(2, |num| *num % 3 == 0); +/// assert_eq!(iter.next(), Some(&[50][..])); +/// assert_eq!(iter.next(), Some(&[10, 40, 30, 20][..])); +/// assert_eq!(iter.next(), None); /// ``` /// /// [`rsplitn`]: slice::rsplitn @@ -1300,7 +1313,11 @@ forward_iterator! { RSplitNMut: T, &'a mut [T] } /// /// ``` /// let slice = ['r', 'u', 's', 't']; -/// let iter = slice.windows(2); +/// let mut iter = slice.windows(2); +/// assert_eq!(iter.next(), Some(&['r', 'u'][..])); +/// assert_eq!(iter.next(), Some(&['u', 's'][..])); +/// assert_eq!(iter.next(), Some(&['s', 't'][..])); +/// assert_eq!(iter.next(), None); /// ``` /// /// [`windows`]: slice::windows @@ -1448,7 +1465,11 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for Windows<'a, T> { /// /// ``` /// let slice = ['l', 'o', 'r', 'e', 'm']; -/// let iter = slice.chunks(2); +/// let mut iter = slice.chunks(2); +/// assert_eq!(iter.next(), Some(&['l', 'o'][..])); +/// assert_eq!(iter.next(), Some(&['r', 'e'][..])); +/// assert_eq!(iter.next(), Some(&['m'][..])); +/// assert_eq!(iter.next(), None); /// ``` /// /// [`chunks`]: slice::chunks @@ -1819,7 +1840,10 @@ unsafe impl Sync for ChunksMut<'_, T> where T: Sync {} /// /// ``` /// let slice = ['l', 'o', 'r', 'e', 'm']; -/// let iter = slice.chunks_exact(2); +/// let mut iter = slice.chunks_exact(2); +/// assert_eq!(iter.next(), Some(&['l', 'o'][..])); +/// assert_eq!(iter.next(), Some(&['r', 'e'][..])); +/// assert_eq!(iter.next(), None); /// ``` /// /// [`chunks_exact`]: slice::chunks_exact @@ -2163,7 +2187,11 @@ unsafe impl Sync for ChunksExactMut<'_, T> where T: Sync {} /// #![feature(array_windows)] /// /// let slice = [0, 1, 2, 3]; -/// let iter = slice.array_windows::<2>(); +/// let mut iter = slice.array_windows::<2>(); +/// assert_eq!(iter.next(), Some(&[0, 1])); +/// assert_eq!(iter.next(), Some(&[1, 2])); +/// assert_eq!(iter.next(), Some(&[2, 3])); +/// assert_eq!(iter.next(), None); /// ``` /// /// [`array_windows`]: slice::array_windows @@ -2285,7 +2313,10 @@ impl ExactSizeIterator for ArrayWindows<'_, T, N> { /// #![feature(array_chunks)] /// /// let slice = ['l', 'o', 'r', 'e', 'm']; -/// let iter = slice.array_chunks::<2>(); +/// let mut iter = slice.array_chunks::<2>(); +/// assert_eq!(iter.next(), Some(&['l', 'o'])); +/// assert_eq!(iter.next(), Some(&['r', 'e'])); +/// assert_eq!(iter.next(), None); /// ``` /// /// [`array_chunks`]: slice::array_chunks @@ -2526,7 +2557,11 @@ unsafe impl<'a, T, const N: usize> TrustedRandomAccessNoCoerce for ArrayChunksMu /// /// ``` /// let slice = ['l', 'o', 'r', 'e', 'm']; -/// let iter = slice.rchunks(2); +/// let mut iter = slice.rchunks(2); +/// assert_eq!(iter.next(), Some(&['e', 'm'][..])); +/// assert_eq!(iter.next(), Some(&['o', 'r'][..])); +/// assert_eq!(iter.next(), Some(&['l'][..])); +/// assert_eq!(iter.next(), None); /// ``` /// /// [`rchunks`]: slice::rchunks @@ -2892,7 +2927,10 @@ unsafe impl Sync for RChunksMut<'_, T> where T: Sync {} /// /// ``` /// let slice = ['l', 'o', 'r', 'e', 'm']; -/// let iter = slice.rchunks_exact(2); +/// let mut iter = slice.rchunks_exact(2); +/// assert_eq!(iter.next(), Some(&['e', 'm'][..])); +/// assert_eq!(iter.next(), Some(&['o', 'r'][..])); +/// assert_eq!(iter.next(), None); /// ``` /// /// [`rchunks_exact`]: slice::rchunks_exact diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index 0b8ff5cc01242..c2a3819464410 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -103,7 +103,8 @@ macro_rules! iterator { // so this new pointer is inside `self` and thus guaranteed to be non-null. unsafe { if_zst!(mut self, - len => *len = len.unchecked_sub(offset), + // Using the intrinsic directly avoids emitting a UbCheck + len => *len = crate::intrinsics::unchecked_sub(*len, offset), _end => self.ptr = self.ptr.add(offset), ); } @@ -119,7 +120,8 @@ macro_rules! iterator { // SAFETY: By our precondition, `offset` can be at most the // current length, so the subtraction can never overflow. len => unsafe { - *len = len.unchecked_sub(offset); + // Using the intrinsic directly avoids emitting a UbCheck + *len = crate::intrinsics::unchecked_sub(*len, offset); self.ptr }, // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`, diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index f82f965e67cf4..b1440214d795a 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -7,16 +7,13 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::cmp::Ordering::{self, Equal, Greater, Less}; -use crate::fmt; -use crate::hint; -use crate::intrinsics::{exact_div, unchecked_sub}; +use crate::intrinsics::{exact_div, select_unpredictable, unchecked_sub}; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::{Bound, OneSidedRange, Range, RangeBounds}; -use crate::ptr; use crate::simd::{self, Simd}; -use crate::slice; use crate::ub_checks::assert_unsafe_precondition; +use crate::{fmt, hint, ptr, slice}; #[unstable( feature = "slice_internals", @@ -39,62 +36,43 @@ pub(crate) mod index; mod iter; mod raw; mod rotate; -mod select; mod specialize; #[unstable(feature = "str_internals", issue = "none")] #[doc(hidden)] pub use ascii::is_ascii_simple; - +#[stable(feature = "inherent_ascii_escape", since = "1.60.0")] +pub use ascii::EscapeAscii; +#[stable(feature = "slice_get_slice", since = "1.28.0")] +pub use index::SliceIndex; +#[unstable(feature = "slice_range", issue = "76393")] +pub use index::{range, try_range}; +#[unstable(feature = "array_windows", issue = "75027")] +pub use iter::ArrayWindows; +#[unstable(feature = "array_chunks", issue = "74985")] +pub use iter::{ArrayChunks, ArrayChunksMut}; +#[stable(feature = "slice_group_by", since = "1.77.0")] +pub use iter::{ChunkBy, ChunkByMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use iter::{Chunks, ChunksMut, Windows}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use iter::{Iter, IterMut}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use iter::{RSplitN, RSplitNMut, Split, SplitMut, SplitN, SplitNMut}; - -#[stable(feature = "slice_rsplit", since = "1.27.0")] -pub use iter::{RSplit, RSplitMut}; - #[stable(feature = "chunks_exact", since = "1.31.0")] pub use iter::{ChunksExact, ChunksExactMut}; - +#[stable(feature = "rust1", since = "1.0.0")] +pub use iter::{Iter, IterMut}; #[stable(feature = "rchunks", since = "1.31.0")] pub use iter::{RChunks, RChunksExact, RChunksExactMut, RChunksMut}; - -#[unstable(feature = "array_chunks", issue = "74985")] -pub use iter::{ArrayChunks, ArrayChunksMut}; - -#[unstable(feature = "array_windows", issue = "75027")] -pub use iter::ArrayWindows; - -#[stable(feature = "slice_group_by", since = "1.77.0")] -pub use iter::{ChunkBy, ChunkByMut}; - +#[stable(feature = "slice_rsplit", since = "1.27.0")] +pub use iter::{RSplit, RSplitMut}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use iter::{RSplitN, RSplitNMut, Split, SplitMut, SplitN, SplitNMut}; #[stable(feature = "split_inclusive", since = "1.51.0")] pub use iter::{SplitInclusive, SplitInclusiveMut}; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use raw::{from_raw_parts, from_raw_parts_mut}; - #[stable(feature = "from_ref", since = "1.28.0")] pub use raw::{from_mut, from_ref}; - #[unstable(feature = "slice_from_ptr_range", issue = "89792")] pub use raw::{from_mut_ptr_range, from_ptr_range}; - -// This function is public only because there is no other way to unit test heapsort. -#[unstable(feature = "sort_internals", reason = "internal to sort module", issue = "none")] -pub use sort::heapsort; - -#[stable(feature = "slice_get_slice", since = "1.28.0")] -pub use index::SliceIndex; - -#[unstable(feature = "slice_range", issue = "76393")] -pub use index::{range, try_range}; - -#[stable(feature = "inherent_ascii_escape", since = "1.60.0")] -pub use ascii::EscapeAscii; +#[stable(feature = "rust1", since = "1.0.0")] +pub use raw::{from_raw_parts, from_raw_parts_mut}; /// Calculates the direction and split point of a one-sided range. /// @@ -326,7 +304,7 @@ impl [T] { if let [.., last] = self { Some(last) } else { None } } - /// Return an array reference to the first `N` items in the slice. + /// Returns an array reference to the first `N` items in the slice. /// /// If the slice is not at least `N` in length, this will return `None`. /// @@ -355,7 +333,7 @@ impl [T] { } } - /// Return a mutable array reference to the first `N` items in the slice. + /// Returns a mutable array reference to the first `N` items in the slice. /// /// If the slice is not at least `N` in length, this will return `None`. /// @@ -386,7 +364,7 @@ impl [T] { } } - /// Return an array reference to the first `N` items in the slice and the remaining slice. + /// Returns an array reference to the first `N` items in the slice and the remaining slice. /// /// If the slice is not at least `N` in length, this will return `None`. /// @@ -418,7 +396,7 @@ impl [T] { } } - /// Return a mutable array reference to the first `N` items in the slice and the remaining + /// Returns a mutable array reference to the first `N` items in the slice and the remaining /// slice. /// /// If the slice is not at least `N` in length, this will return `None`. @@ -456,7 +434,7 @@ impl [T] { } } - /// Return an array reference to the last `N` items in the slice and the remaining slice. + /// Returns an array reference to the last `N` items in the slice and the remaining slice. /// /// If the slice is not at least `N` in length, this will return `None`. /// @@ -488,7 +466,7 @@ impl [T] { } } - /// Return a mutable array reference to the last `N` items in the slice and the remaining + /// Returns a mutable array reference to the last `N` items in the slice and the remaining /// slice. /// /// If the slice is not at least `N` in length, this will return `None`. @@ -526,7 +504,7 @@ impl [T] { } } - /// Return an array reference to the last `N` items in the slice. + /// Returns an array reference to the last `N` items in the slice. /// /// If the slice is not at least `N` in length, this will return `None`. /// @@ -544,7 +522,7 @@ impl [T] { /// ``` #[inline] #[stable(feature = "slice_first_last_chunk", since = "1.77.0")] - #[rustc_const_stable(feature = "slice_first_last_chunk", since = "1.77.0")] + #[rustc_const_stable(feature = "const_slice_last_chunk", since = "1.80.0")] pub const fn last_chunk(&self) -> Option<&[T; N]> { if self.len() < N { None @@ -559,7 +537,7 @@ impl [T] { } } - /// Return a mutable array reference to the last `N` items in the slice. + /// Returns a mutable array reference to the last `N` items in the slice. /// /// If the slice is not at least `N` in length, this will return `None`. /// @@ -731,7 +709,7 @@ impl [T] { /// Returns a raw pointer to the slice's buffer. /// /// The caller must ensure that the slice outlives the pointer this - /// function returns, or else it will end up pointing to garbage. + /// function returns, or else it will end up dangling. /// /// The caller must also ensure that the memory the pointer (non-transitively) points to /// is never written to (except inside an `UnsafeCell`) using this pointer or any pointer @@ -766,7 +744,7 @@ impl [T] { /// Returns an unsafe mutable pointer to the slice's buffer. /// /// The caller must ensure that the slice outlives the pointer this - /// function returns, or else it will end up pointing to garbage. + /// function returns, or else it will end up dangling. /// /// Modifying the container referenced by this slice may cause its buffer /// to be reallocated, which would also make any pointers to it invalid. @@ -833,7 +811,7 @@ impl [T] { // - Both pointers are part of the same object, as pointing directly // past the object also counts. // - // - The size of the slice is never larger than isize::MAX bytes, as + // - The size of the slice is never larger than `isize::MAX` bytes, as // noted here: // - https://github.com/rust-lang/unsafe-code-guidelines/issues/102#issuecomment-473340447 // - https://doc.rust-lang.org/reference/behavior-considered-undefined.html @@ -844,7 +822,7 @@ impl [T] { // - There is no wrapping around involved, as slices do not wrap past // the end of the address space. // - // See the documentation of pointer::add. + // See the documentation of [`pointer::add`]. let end = unsafe { start.add(self.len()) }; start..end } @@ -2082,8 +2060,8 @@ impl [T] { /// /// assert_eq!(None, v.split_at_checked(7)); /// ``` - #[stable(feature = "split_at_checked", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "split_at_checked", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "split_at_checked", since = "1.80.0")] + #[rustc_const_stable(feature = "split_at_checked", since = "1.80.0")] #[inline] #[must_use] pub const fn split_at_checked(&self, mid: usize) -> Option<(&[T], &[T])> { @@ -2121,7 +2099,7 @@ impl [T] { /// /// assert_eq!(None, v.split_at_mut_checked(7)); /// ``` - #[stable(feature = "split_at_checked", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "split_at_checked", since = "1.80.0")] #[rustc_const_unstable(feature = "const_slice_split_at_mut", issue = "101804")] #[inline] #[must_use] @@ -2792,41 +2770,54 @@ impl [T] { where F: FnMut(&'a T) -> Ordering, { - // INVARIANTS: - // - 0 <= left <= left + size = right <= self.len() - // - f returns Less for everything in self[..left] - // - f returns Greater for everything in self[right..] let mut size = self.len(); - let mut left = 0; - let mut right = size; - while left < right { - let mid = left + size / 2; - - // SAFETY: the while condition means `size` is strictly positive, so - // `size/2 < size`. Thus `left + size/2 < left + size`, which - // coupled with the `left + size <= self.len()` invariant means - // we have `left + size/2 < self.len()`, and this is in-bounds. + if size == 0 { + return Err(0); + } + let mut base = 0usize; + + // This loop intentionally doesn't have an early exit if the comparison + // returns Equal. We want the number of loop iterations to depend *only* + // on the size of the input slice so that the CPU can reliably predict + // the loop count. + while size > 1 { + let half = size / 2; + let mid = base + half; + + // SAFETY: the call is made safe by the following inconstants: + // - `mid >= 0`: by definition + // - `mid < size`: `mid = size / 2 + size / 4 + size / 8 ...` let cmp = f(unsafe { self.get_unchecked(mid) }); - // This control flow produces conditional moves, which results in - // fewer branches and instructions than if/else or matching on - // cmp::Ordering. - // This is x86 asm for u8: https://rust.godbolt.org/z/698eYffTx. - left = if cmp == Less { mid + 1 } else { left }; - right = if cmp == Greater { mid } else { right }; - if cmp == Equal { - // SAFETY: same as the `get_unchecked` above - unsafe { hint::assert_unchecked(mid < self.len()) }; - return Ok(mid); - } - - size = right - left; + // Binary search interacts poorly with branch prediction, so force + // the compiler to use conditional moves if supported by the target + // architecture. + base = select_unpredictable(cmp == Greater, base, mid); + + // This is imprecise in the case where `size` is odd and the + // comparison returns Greater: the mid element still gets included + // by `size` even though it's known to be larger than the element + // being searched for. + // + // This is fine though: we gain more performance by keeping the + // loop iteration count invariant (and thus predictable) than we + // lose from considering one additional element. + size -= half; } - // SAFETY: directly true from the overall invariant. - // Note that this is `<=`, unlike the assume in the `Ok` path. - unsafe { hint::assert_unchecked(left <= self.len()) }; - Err(left) + // SAFETY: base is always in [0, size) because base <= mid. + let cmp = f(unsafe { self.get_unchecked(base) }); + if cmp == Equal { + // SAFETY: same as the `get_unchecked` above. + unsafe { hint::assert_unchecked(base < self.len()) }; + Ok(base) + } else { + let result = base + (cmp == Less) as usize; + // SAFETY: same as the `get_unchecked` above. + // Note that this is `<=`, unlike the assume in the `Ok` path. + unsafe { hint::assert_unchecked(result <= self.len()) }; + Err(result) + } } /// Binary searches this slice with a key extraction function. @@ -2884,21 +2875,26 @@ impl [T] { self.binary_search_by(|k| f(k).cmp(b)) } - /// Sorts the slice, but might not preserve the order of equal elements. + /// Sorts the slice **without** preserving the initial order of equal elements. /// - /// This sort is unstable (i.e., may reorder equal elements), in-place - /// (i.e., does not allocate), and *O*(*n* \* log(*n*)) worst-case. + /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not + /// allocate), and *O*(*n* \* log(*n*)) worst-case. + /// + /// If `T: Ord` does not implement a total order the resulting order is unspecified. All + /// original elements will remain in the slice and any possible modifications via interior + /// mutability are observed in the input. Same is true if `T: Ord` panics. /// /// # Current implementation /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. + /// The current implementation is based on [ipnsort] by Lukas Bergdoll and Orson Peters, which + /// combines the fast average case of quicksort with the fast worst case of heapsort, achieving + /// linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the + /// expected time to sort the data is *O*(*n* \* log(*k*)). /// /// It is typically faster than stable sorting, except in a few special cases, e.g., when the - /// slice consists of several concatenated sorted sequences. + /// slice is partially sorted. + /// + /// If `T: Ord` does not implement a total order, the implementation may panic. /// /// # Examples /// @@ -2909,25 +2905,29 @@ impl [T] { /// assert!(v == [-5, -3, 1, 2, 4]); /// ``` /// - /// [pdqsort]: https://github.com/orlp/pdqsort + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable(&mut self) where T: Ord, { - sort::quicksort(self, T::lt); + sort::unstable::sort(self, &mut T::lt); } - /// Sorts the slice with a comparator function, but might not preserve the order of equal - /// elements. + /// Sorts the slice with a comparator function, **without** preserving the initial order of + /// equal elements. /// - /// This sort is unstable (i.e., may reorder equal elements), in-place - /// (i.e., does not allocate), and *O*(*n* \* log(*n*)) worst-case. + /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not + /// allocate), and *O*(*n* \* log(*n*)) worst-case. /// - /// The comparator function must define a total ordering for the elements in the slice. If - /// the ordering is not total, the order of the elements is unspecified. An order is a - /// total order if it is (for all `a`, `b` and `c`): + /// The comparator function should define a total ordering for the elements in the slice. If the + /// ordering is not total, the order of the elements is unspecified. + /// + /// If the comparator function does not implement a total order the resulting order is + /// unspecified. All original elements will remain in the slice and any possible modifications + /// via interior mutability are observed in the input. Same is true if the comparator function + /// panics. A total order (for all `a`, `b` and `c`): /// /// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and /// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. @@ -2943,14 +2943,15 @@ impl [T] { /// /// # Current implementation /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. + /// The current implementation is based on [ipnsort] by Lukas Bergdoll and Orson Peters, which + /// combines the fast average case of quicksort with the fast worst case of heapsort, achieving + /// linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the + /// expected time to sort the data is *O*(*n* \* log(*k*)). /// /// It is typically faster than stable sorting, except in a few special cases, e.g., when the - /// slice consists of several concatenated sorted sequences. + /// slice is partially sorted. + /// + /// If `T: Ord` does not implement a total order, the implementation may panic. /// /// # Examples /// @@ -2964,34 +2965,37 @@ impl [T] { /// assert!(v == [5, 4, 3, 2, 1]); /// ``` /// - /// [pdqsort]: https://github.com/orlp/pdqsort + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable_by(&mut self, mut compare: F) where F: FnMut(&T, &T) -> Ordering, { - sort::quicksort(self, |a, b| compare(a, b) == Ordering::Less); + sort::unstable::sort(self, &mut |a, b| compare(a, b) == Ordering::Less); } - /// Sorts the slice with a key extraction function, but might not preserve the order of equal - /// elements. + /// Sorts the slice with a key extraction function, **without** preserving the initial order of + /// equal elements. + /// + /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not + /// allocate), and *O*(*n* \* log(*n*)) worst-case. /// - /// This sort is unstable (i.e., may reorder equal elements), in-place - /// (i.e., does not allocate), and *O*(*m* \* *n* \* log(*n*)) worst-case, where the key function is - /// *O*(*m*). + /// If `K: Ord` does not implement a total order the resulting order is unspecified. + /// All original elements will remain in the slice and any possible modifications via interior + /// mutability are observed in the input. Same is true if `K: Ord` panics. /// /// # Current implementation /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. + /// The current implementation is based on [ipnsort] by Lukas Bergdoll and Orson Peters, which + /// combines the fast average case of quicksort with the fast worst case of heapsort, achieving + /// linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the + /// expected time to sort the data is *O*(*n* \* log(*k*)). + /// + /// It is typically faster than stable sorting, except in a few special cases, e.g., when the + /// slice is partially sorted. /// - /// Due to its key calling strategy, [`sort_unstable_by_key`](#method.sort_unstable_by_key) - /// is likely to be slower than [`sort_by_cached_key`](#method.sort_by_cached_key) in - /// cases where the key function is expensive. + /// If `K: Ord` does not implement a total order, the implementation may panic. /// /// # Examples /// @@ -3002,7 +3006,7 @@ impl [T] { /// assert!(v == [1, 2, -3, 4, -5]); /// ``` /// - /// [pdqsort]: https://github.com/orlp/pdqsort + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable_by_key(&mut self, mut f: F) @@ -3010,27 +3014,32 @@ impl [T] { F: FnMut(&T) -> K, K: Ord, { - sort::quicksort(self, |a, b| f(a).lt(&f(b))); + sort::unstable::sort(self, &mut |a, b| f(a).lt(&f(b))); } - /// Reorder the slice such that the element at `index` after the reordering is at its final sorted position. + /// Reorders the slice such that the element at `index` after the reordering is at its final + /// sorted position. /// /// This reordering has the additional property that any value at position `i < index` will be /// less than or equal to any value at a position `j > index`. Additionally, this reordering is - /// unstable (i.e. any number of equal elements may end up at position `index`), in-place - /// (i.e. does not allocate), and runs in *O*(*n*) time. - /// This function is also known as "kth element" in other libraries. + /// unstable (i.e. any number of equal elements may end up at position `index`), in-place (i.e. + /// does not allocate), and runs in *O*(*n*) time. This function is also known as "kth element" + /// in other libraries. /// - /// It returns a triplet of the following from the reordered slice: - /// the subslice prior to `index`, the element at `index`, and the subslice after `index`; - /// accordingly, the values in those two subslices will respectively all be less-than-or-equal-to - /// and greater-than-or-equal-to the value of the element at `index`. + /// It returns a triplet of the following from the reordered slice: the subslice prior to + /// `index`, the element at `index`, and the subslice after `index`; accordingly, the values in + /// those two subslices will respectively all be less-than-or-equal-to and + /// greater-than-or-equal-to the value of the element at `index`. /// /// # Current implementation /// - /// The current algorithm is an introselect implementation based on Pattern Defeating Quicksort, which is also - /// the basis for [`sort_unstable`]. The fallback algorithm is Median of Medians using Tukey's Ninther for - /// pivot selection, which guarantees linear runtime for all inputs. + /// The current algorithm is an introselect implementation based on [ipnsort] by Lukas Bergdoll + /// and Orson Peters, which is also the basis for [`sort_unstable`]. The fallback algorithm is + /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime + /// for all inputs. + /// + /// It is typically faster than stable sorting, except in a few special cases, e.g., when the + /// slice is nearly fully sorted, where `slice::sort` may be faster. /// /// [`sort_unstable`]: slice::sort_unstable /// @@ -3058,35 +3067,40 @@ impl [T] { /// v == [-3, -5, 1, 4, 2] || /// v == [-5, -3, 1, 4, 2]); /// ``` + /// + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] pub fn select_nth_unstable(&mut self, index: usize) -> (&mut [T], &mut T, &mut [T]) where T: Ord, { - select::partition_at_index(self, index, T::lt) + sort::select::partition_at_index(self, index, T::lt) } - /// Reorder the slice with a comparator function such that the element at `index` after the reordering is at - /// its final sorted position. + /// Reorders the slice with a comparator function such that the element at `index` after the + /// reordering is at its final sorted position. /// /// This reordering has the additional property that any value at position `i < index` will be /// less than or equal to any value at a position `j > index` using the comparator function. /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at - /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. - /// This function is also known as "kth element" in other libraries. + /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This + /// function is also known as "kth element" in other libraries. /// - /// It returns a triplet of the following from - /// the slice reordered according to the provided comparator function: the subslice prior to - /// `index`, the element at `index`, and the subslice after `index`; accordingly, the values in - /// those two subslices will respectively all be less-than-or-equal-to and greater-than-or-equal-to - /// the value of the element at `index`. + /// It returns a triplet of the following from the slice reordered according to the provided + /// comparator function: the subslice prior to `index`, the element at `index`, and the subslice + /// after `index`; accordingly, the values in those two subslices will respectively all be + /// less-than-or-equal-to and greater-than-or-equal-to the value of the element at `index`. /// /// # Current implementation /// - /// The current algorithm is an introselect implementation based on Pattern Defeating Quicksort, which is also - /// the basis for [`sort_unstable`]. The fallback algorithm is Median of Medians using Tukey's Ninther for - /// pivot selection, which guarantees linear runtime for all inputs. + /// The current algorithm is an introselect implementation based on [ipnsort] by Lukas Bergdoll + /// and Orson Peters, which is also the basis for [`sort_unstable`]. The fallback algorithm is + /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime + /// for all inputs. + /// + /// It is typically faster than stable sorting, except in a few special cases, e.g., when the + /// slice is nearly fully sorted, where `slice::sort` may be faster. /// /// [`sort_unstable`]: slice::sort_unstable /// @@ -3114,6 +3128,8 @@ impl [T] { /// v == [4, 2, 1, -5, -3] || /// v == [4, 2, 1, -3, -5]); /// ``` + /// + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] pub fn select_nth_unstable_by( @@ -3124,29 +3140,32 @@ impl [T] { where F: FnMut(&T, &T) -> Ordering, { - select::partition_at_index(self, index, |a: &T, b: &T| compare(a, b) == Less) + sort::select::partition_at_index(self, index, |a: &T, b: &T| compare(a, b) == Less) } - /// Reorder the slice with a key extraction function such that the element at `index` after the reordering is - /// at its final sorted position. + /// Reorders the slice with a key extraction function such that the element at `index` after the + /// reordering is at its final sorted position. /// /// This reordering has the additional property that any value at position `i < index` will be /// less than or equal to any value at a position `j > index` using the key extraction function. /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at - /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. - /// This function is also known as "kth element" in other libraries. + /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This + /// function is also known as "kth element" in other libraries. /// - /// It returns a triplet of the following from - /// the slice reordered according to the provided key extraction function: the subslice prior to - /// `index`, the element at `index`, and the subslice after `index`; accordingly, the values in - /// those two subslices will respectively all be less-than-or-equal-to and greater-than-or-equal-to - /// the value of the element at `index`. + /// It returns a triplet of the following from the slice reordered according to the provided key + /// extraction function: the subslice prior to `index`, the element at `index`, and the subslice + /// after `index`; accordingly, the values in those two subslices will respectively all be + /// less-than-or-equal-to and greater-than-or-equal-to the value of the element at `index`. /// /// # Current implementation /// - /// The current algorithm is an introselect implementation based on Pattern Defeating Quicksort, which is also - /// the basis for [`sort_unstable`]. The fallback algorithm is Median of Medians using Tukey's Ninther for - /// pivot selection, which guarantees linear runtime for all inputs. + /// The current algorithm is an introselect implementation based on [ipnsort] by Lukas Bergdoll + /// and Orson Peters, which is also the basis for [`sort_unstable`]. The fallback algorithm is + /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime + /// for all inputs. + /// + /// It is typically faster than stable sorting, except in a few special cases, e.g., when the + /// slice is nearly fully sorted, where `slice::sort` may be faster. /// /// [`sort_unstable`]: slice::sort_unstable /// @@ -3174,6 +3193,8 @@ impl [T] { /// v == [2, 1, -3, 4, -5] || /// v == [2, 1, -3, -5, 4]); /// ``` + /// + /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] pub fn select_nth_unstable_by_key( @@ -3185,7 +3206,7 @@ impl [T] { F: FnMut(&T) -> K, K: Ord, { - select::partition_at_index(self, index, |a: &T, b: &T| f(a).lt(&f(b))) + sort::select::partition_at_index(self, index, |a: &T, b: &T| f(a).lt(&f(b))) } /// Moves all consecutive repeated elements to the end of the slice according to the @@ -3380,8 +3401,10 @@ impl [T] { /// Rotates the slice in-place such that the first `mid` elements of the /// slice move to the end while the last `self.len() - mid` elements move to - /// the front. After calling `rotate_left`, the element previously at index - /// `mid` will become the first element in the slice. + /// the front. + /// + /// After calling `rotate_left`, the element previously at index `mid` will + /// become the first element in the slice. /// /// # Panics /// @@ -3423,8 +3446,10 @@ impl [T] { /// Rotates the slice in-place such that the first `self.len() - k` /// elements of the slice move to the end while the last `k` elements move - /// to the front. After calling `rotate_right`, the element previously at - /// index `self.len() - k` will become the first element in the slice. + /// to the front. + /// + /// After calling `rotate_right`, the element previously at index + /// `self.len() - k` will become the first element in the slice. /// /// # Panics /// @@ -3794,7 +3819,7 @@ impl [T] { (us_len, ts_len) } - /// Transmute the slice to a slice of another type, ensuring alignment of the types is + /// Transmutes the slice to a slice of another type, ensuring alignment of the types is /// maintained. /// /// This method splits the slice into three distinct slices: prefix, correctly aligned middle @@ -3859,7 +3884,7 @@ impl [T] { } } - /// Transmute the mutable slice to a mutable slice of another type, ensuring alignment of the + /// Transmutes the mutable slice to a mutable slice of another type, ensuring alignment of the /// types is maintained. /// /// This method splits the slice into three distinct slices: prefix, correctly aligned middle @@ -3932,19 +3957,10 @@ impl [T] { } } - /// Split a slice into a prefix, a middle of aligned SIMD types, and a suffix. - /// - /// This is a safe wrapper around [`slice::align_to`], so has the same weak - /// postconditions as that method. You're only assured that - /// `self.len() == prefix.len() + middle.len() * LANES + suffix.len()`. - /// - /// Notably, all of the following are possible: - /// - `prefix.len() >= LANES`. - /// - `middle.is_empty()` despite `self.len() >= 3 * LANES`. - /// - `suffix.len() >= LANES`. + /// Splits a slice into a prefix, a middle of aligned SIMD types, and a suffix. /// - /// That said, this is a safe method, so if you're only writing safe code, - /// then this can at most cause incorrect logic, not unsoundness. + /// This is a safe wrapper around [`slice::align_to`], so inherits the same + /// guarantees as that method. /// /// # Panics /// @@ -4005,20 +4021,11 @@ impl [T] { unsafe { self.align_to() } } - /// Split a mutable slice into a mutable prefix, a middle of aligned SIMD types, + /// Splits a mutable slice into a mutable prefix, a middle of aligned SIMD types, /// and a mutable suffix. /// - /// This is a safe wrapper around [`slice::align_to_mut`], so has the same weak - /// postconditions as that method. You're only assured that - /// `self.len() == prefix.len() + middle.len() * LANES + suffix.len()`. - /// - /// Notably, all of the following are possible: - /// - `prefix.len() >= LANES`. - /// - `middle.is_empty()` despite `self.len() >= 3 * LANES`. - /// - `suffix.len() >= LANES`. - /// - /// That said, this is a safe method, so if you're only writing safe code, - /// then this can at most cause incorrect logic, not unsoundness. + /// This is a safe wrapper around [`slice::align_to_mut`], so inherits the same + /// guarantees as that method. /// /// This is the mutable version of [`slice::as_simd`]; see that for examples. /// @@ -4062,7 +4069,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(is_sorted)] /// let empty: [i32; 0] = []; /// /// assert!([1, 2, 2, 9].is_sorted()); @@ -4072,7 +4078,7 @@ impl [T] { /// assert!(![0.0, 1.0, f32::NAN].is_sorted()); /// ``` #[inline] - #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] + #[stable(feature = "is_sorted", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn is_sorted(&self) -> bool where @@ -4089,8 +4095,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(is_sorted)] - /// /// assert!([1, 2, 2, 9].is_sorted_by(|a, b| a <= b)); /// assert!(![1, 2, 2, 9].is_sorted_by(|a, b| a < b)); /// @@ -4101,7 +4105,7 @@ impl [T] { /// assert!(empty.is_sorted_by(|a, b| false)); /// assert!(empty.is_sorted_by(|a, b| true)); /// ``` - #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] + #[stable(feature = "is_sorted", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn is_sorted_by<'a, F>(&'a self, mut compare: F) -> bool where @@ -4121,13 +4125,11 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(is_sorted)] - /// /// assert!(["c", "bb", "aaa"].is_sorted_by_key(|s| s.len())); /// assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs())); /// ``` #[inline] - #[unstable(feature = "is_sorted", reason = "new API", issue = "53485")] + #[stable(feature = "is_sorted", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn is_sorted_by_key<'a, F, K>(&'a self, f: F) -> bool where @@ -4515,6 +4517,121 @@ impl [T] { // are disjunct and in bounds. unsafe { Ok(self.get_many_unchecked_mut(indices)) } } + + /// Returns the index that an element reference points to. + /// + /// Returns `None` if `element` does not point within the slice or if it points between elements. + /// + /// This method is useful for extending slice iterators like [`slice::split`]. + /// + /// Note that this uses pointer arithmetic and **does not compare elements**. + /// To find the index of an element via comparison, use + /// [`.iter().position()`](crate::iter::Iterator::position) instead. + /// + /// # Panics + /// Panics if `T` is zero-sized. + /// + /// # Examples + /// Basic usage: + /// ``` + /// #![feature(substr_range)] + /// + /// let nums: &[u32] = &[1, 7, 1, 1]; + /// let num = &nums[2]; + /// + /// assert_eq!(num, &1); + /// assert_eq!(nums.elem_offset(num), Some(2)); + /// ``` + /// Returning `None` with an in-between element: + /// ``` + /// #![feature(substr_range)] + /// + /// let arr: &[[u32; 2]] = &[[0, 1], [2, 3]]; + /// let flat_arr: &[u32] = arr.as_flattened(); + /// + /// let ok_elm: &[u32; 2] = flat_arr[0..2].try_into().unwrap(); + /// let weird_elm: &[u32; 2] = flat_arr[1..3].try_into().unwrap(); + /// + /// assert_eq!(ok_elm, &[0, 1]); + /// assert_eq!(weird_elm, &[1, 2]); + /// + /// assert_eq!(arr.elem_offset(ok_elm), Some(0)); // Points to element 0 + /// assert_eq!(arr.elem_offset(weird_elm), None); // Points between element 0 and 1 + /// ``` + #[must_use] + #[unstable(feature = "substr_range", issue = "126769")] + pub fn elem_offset(&self, element: &T) -> Option { + if T::IS_ZST { + panic!("elements are zero-sized"); + } + + let self_start = self.as_ptr() as usize; + let elem_start = element as *const T as usize; + + let byte_offset = elem_start.wrapping_sub(self_start); + + if byte_offset % mem::size_of::() != 0 { + return None; + } + + let offset = byte_offset / mem::size_of::(); + + if offset < self.len() { Some(offset) } else { None } + } + + /// Returns the range of indices that a subslice points to. + /// + /// Returns `None` if `subslice` does not point within the slice or if it points between elements. + /// + /// This method **does not compare elements**. Instead, this method finds the location in the slice that + /// `subslice` was obtained from. To find the index of a subslice via comparison, instead use + /// [`.windows()`](slice::windows)[`.position()`](crate::iter::Iterator::position). + /// + /// This method is useful for extending slice iterators like [`slice::split`]. + /// + /// Note that this may return a false positive (either `Some(0..0)` or `Some(self.len()..self.len())`) + /// if `subslice` has a length of zero and points to the beginning or end of another, separate, slice. + /// + /// # Panics + /// Panics if `T` is zero-sized. + /// + /// # Examples + /// Basic usage: + /// ``` + /// #![feature(substr_range)] + /// + /// let nums = &[0, 5, 10, 0, 0, 5]; + /// + /// let mut iter = nums + /// .split(|t| *t == 0) + /// .map(|n| nums.subslice_range(n).unwrap()); + /// + /// assert_eq!(iter.next(), Some(0..0)); + /// assert_eq!(iter.next(), Some(1..3)); + /// assert_eq!(iter.next(), Some(4..4)); + /// assert_eq!(iter.next(), Some(5..6)); + /// ``` + #[must_use] + #[unstable(feature = "substr_range", issue = "126769")] + pub fn subslice_range(&self, subslice: &[T]) -> Option> { + if T::IS_ZST { + panic!("elements are zero-sized"); + } + + let self_start = self.as_ptr() as usize; + let subslice_start = subslice.as_ptr() as usize; + + let byte_start = subslice_start.wrapping_sub(self_start); + + if byte_start % core::mem::size_of::() != 0 { + return None; + } + + let start = byte_start / core::mem::size_of::(); + let end = start.wrapping_add(subslice.len()); + + if start <= self.len() && end <= self.len() { Some(start..end) } else { None } + } } impl [[T; N]] { @@ -4531,8 +4648,6 @@ impl [[T; N]] { /// # Examples /// /// ``` - /// #![feature(slice_flatten)] - /// /// assert_eq!([[1, 2, 3], [4, 5, 6]].as_flattened(), &[1, 2, 3, 4, 5, 6]); /// /// assert_eq!( @@ -4546,7 +4661,8 @@ impl [[T; N]] { /// let empty_slice_of_arrays: &[[u32; 10]] = &[]; /// assert!(empty_slice_of_arrays.as_flattened().is_empty()); /// ``` - #[unstable(feature = "slice_flatten", issue = "95629")] + #[stable(feature = "slice_flatten", since = "1.80.0")] + #[rustc_const_unstable(feature = "const_slice_flatten", issue = "95629")] pub const fn as_flattened(&self) -> &[T] { let len = if T::IS_ZST { self.len().checked_mul(N).expect("slice len overflow") @@ -4572,8 +4688,6 @@ impl [[T; N]] { /// # Examples /// /// ``` - /// #![feature(slice_flatten)] - /// /// fn add_5_to_all(slice: &mut [i32]) { /// for i in slice { /// *i += 5; @@ -4584,7 +4698,7 @@ impl [[T; N]] { /// add_5_to_all(array.as_flattened_mut()); /// assert_eq!(array, [[6, 7, 8], [9, 10, 11], [12, 13, 14]]); /// ``` - #[unstable(feature = "slice_flatten", issue = "95629")] + #[stable(feature = "slice_flatten", since = "1.80.0")] pub fn as_flattened_mut(&mut self) -> &mut [T] { let len = if T::IS_ZST { self.len().checked_mul(N).expect("slice len overflow") diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 29a12f106c5ed..85507eb8a7381 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -1,10 +1,7 @@ //! Free functions to create `&[T]` and `&mut [T]`. -use crate::array; -use crate::mem::{align_of, size_of}; use crate::ops::Range; -use crate::ptr; -use crate::ub_checks; +use crate::{array, ptr, ub_checks}; /// Forms a slice from a pointer and a length. /// @@ -83,6 +80,39 @@ use crate::ub_checks; /// } /// ``` /// +/// ### FFI: Handling null pointers +/// +/// In languages such as C++, pointers to empty collections are not guaranteed to be non-null. +/// When accepting such pointers, they have to be checked for null-ness to avoid undefined +/// behavior. +/// +/// ``` +/// use std::slice; +/// +/// /// Sum the elements of an FFI slice. +/// /// +/// /// # Safety +/// /// +/// /// If ptr is not NULL, it must be correctly aligned and +/// /// point to `len` initialized items of type `f32`. +/// unsafe extern "C" fn sum_slice(ptr: *const f32, len: usize) -> f32 { +/// let data = if ptr.is_null() { +/// // `len` is assumed to be 0. +/// &[] +/// } else { +/// // SAFETY: see function docstring. +/// unsafe { slice::from_raw_parts(ptr, len) } +/// }; +/// data.into_iter().sum() +/// } +/// +/// // This could be the result of C++'s std::vector::data(): +/// let ptr = std::ptr::null(); +/// // And this could be std::vector::size(): +/// let len = 0; +/// assert_eq!(unsafe { sum_slice(ptr, len) }, 0.0); +/// ``` +/// /// [valid]: ptr#safety /// [`NonNull::dangling()`]: ptr::NonNull::dangling #[inline] diff --git a/library/core/src/slice/rotate.rs b/library/core/src/slice/rotate.rs index fa8c238f8e7a2..1e4865a7caad9 100644 --- a/library/core/src/slice/rotate.rs +++ b/library/core/src/slice/rotate.rs @@ -1,6 +1,5 @@ -use crate::cmp; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; -use crate::ptr; +use crate::{cmp, ptr}; /// Rotates the range `[mid-left, mid+right)` such that the element at `mid` becomes the first /// element. Equivalently, rotates the range `left` elements to the left or `right` elements to the @@ -71,7 +70,9 @@ pub unsafe fn ptr_rotate(mut left: usize, mut mid: *mut T, mut right: usize) if (right == 0) || (left == 0) { return; } - if (left + right < 24) || (mem::size_of::() > mem::size_of::<[usize; 4]>()) { + if !cfg!(feature = "optimize_for_size") + && ((left + right < 24) || (mem::size_of::() > mem::size_of::<[usize; 4]>())) + { // Algorithm 1 // Microbenchmarks indicate that the average performance for random shifts is better all // the way until about `left + right == 32`, but the worst case performance breaks even @@ -158,7 +159,9 @@ pub unsafe fn ptr_rotate(mut left: usize, mut mid: *mut T, mut right: usize) } return; // `T` is not a zero-sized type, so it's okay to divide by its size. - } else if cmp::min(left, right) <= mem::size_of::() / mem::size_of::() { + } else if !cfg!(feature = "optimize_for_size") + && cmp::min(left, right) <= mem::size_of::() / mem::size_of::() + { // Algorithm 2 // The `[T; 0]` here is to ensure this is appropriately aligned for T let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); diff --git a/library/core/src/slice/sort.rs b/library/core/src/slice/sort.rs deleted file mode 100644 index 993a608f42b60..0000000000000 --- a/library/core/src/slice/sort.rs +++ /dev/null @@ -1,1383 +0,0 @@ -//! Slice sorting -//! -//! This module contains a sorting algorithm based on Orson Peters' pattern-defeating quicksort, -//! published at: -//! -//! Unstable sorting is compatible with core because it doesn't allocate memory, unlike our -//! stable sorting implementation. -//! -//! In addition it also contains the core logic of the stable sort used by `slice::sort` based on -//! TimSort. - -use crate::cmp; -use crate::mem::{self, MaybeUninit, SizedTypeProperties}; -use crate::ptr; - -// When dropped, copies from `src` into `dest`. -struct InsertionHole { - src: *const T, - dest: *mut T, -} - -impl Drop for InsertionHole { - fn drop(&mut self) { - // SAFETY: This is a helper class. Please refer to its usage for correctness. Namely, one - // must be sure that `src` and `dst` does not overlap as required by - // `ptr::copy_nonoverlapping` and are both valid for writes. - unsafe { - ptr::copy_nonoverlapping(self.src, self.dest, 1); - } - } -} - -/// Inserts `v[v.len() - 1]` into pre-sorted sequence `v[..v.len() - 1]` so that whole `v[..]` -/// becomes sorted. -unsafe fn insert_tail(v: &mut [T], is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - debug_assert!(v.len() >= 2); - - let arr_ptr = v.as_mut_ptr(); - let i = v.len() - 1; - - // SAFETY: caller must ensure v is at least len 2. - unsafe { - // See insert_head which talks about why this approach is beneficial. - let i_ptr = arr_ptr.add(i); - - // It's important that we use i_ptr here. If this check is positive and we continue, - // We want to make sure that no other copy of the value was seen by is_less. - // Otherwise we would have to copy it back. - if is_less(&*i_ptr, &*i_ptr.sub(1)) { - // It's important, that we use tmp for comparison from now on. As it is the value that - // will be copied back. And notionally we could have created a divergence if we copy - // back the wrong value. - let tmp = mem::ManuallyDrop::new(ptr::read(i_ptr)); - // Intermediate state of the insertion process is always tracked by `hole`, which - // serves two purposes: - // 1. Protects integrity of `v` from panics in `is_less`. - // 2. Fills the remaining hole in `v` in the end. - // - // Panic safety: - // - // If `is_less` panics at any point during the process, `hole` will get dropped and - // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it - // initially held exactly once. - let mut hole = InsertionHole { src: &*tmp, dest: i_ptr.sub(1) }; - ptr::copy_nonoverlapping(hole.dest, i_ptr, 1); - - // SAFETY: We know i is at least 1. - for j in (0..(i - 1)).rev() { - let j_ptr = arr_ptr.add(j); - if !is_less(&*tmp, &*j_ptr) { - break; - } - - ptr::copy_nonoverlapping(j_ptr, hole.dest, 1); - hole.dest = j_ptr; - } - // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`. - } - } -} - -/// Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted. -/// -/// This is the integral subroutine of insertion sort. -unsafe fn insert_head(v: &mut [T], is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - debug_assert!(v.len() >= 2); - - // SAFETY: caller must ensure v is at least len 2. - unsafe { - if is_less(v.get_unchecked(1), v.get_unchecked(0)) { - let arr_ptr = v.as_mut_ptr(); - - // There are three ways to implement insertion here: - // - // 1. Swap adjacent elements until the first one gets to its final destination. - // However, this way we copy data around more than is necessary. If elements are big - // structures (costly to copy), this method will be slow. - // - // 2. Iterate until the right place for the first element is found. Then shift the - // elements succeeding it to make room for it and finally place it into the - // remaining hole. This is a good method. - // - // 3. Copy the first element into a temporary variable. Iterate until the right place - // for it is found. As we go along, copy every traversed element into the slot - // preceding it. Finally, copy data from the temporary variable into the remaining - // hole. This method is very good. Benchmarks demonstrated slightly better - // performance than with the 2nd method. - // - // All methods were benchmarked, and the 3rd showed best results. So we chose that one. - let tmp = mem::ManuallyDrop::new(ptr::read(arr_ptr)); - - // Intermediate state of the insertion process is always tracked by `hole`, which - // serves two purposes: - // 1. Protects integrity of `v` from panics in `is_less`. - // 2. Fills the remaining hole in `v` in the end. - // - // Panic safety: - // - // If `is_less` panics at any point during the process, `hole` will get dropped and - // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it - // initially held exactly once. - let mut hole = InsertionHole { src: &*tmp, dest: arr_ptr.add(1) }; - ptr::copy_nonoverlapping(arr_ptr.add(1), arr_ptr.add(0), 1); - - for i in 2..v.len() { - if !is_less(&v.get_unchecked(i), &*tmp) { - break; - } - ptr::copy_nonoverlapping(arr_ptr.add(i), arr_ptr.add(i - 1), 1); - hole.dest = arr_ptr.add(i); - } - // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`. - } - } -} - -/// Sort `v` assuming `v[..offset]` is already sorted. -/// -/// Never inline this function to avoid code bloat. It still optimizes nicely and has practically no -/// performance impact. Even improving performance in some cases. -#[inline(never)] -pub(super) fn insertion_sort_shift_left(v: &mut [T], offset: usize, is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - let len = v.len(); - - // Using assert here improves performance. - assert!(offset != 0 && offset <= len); - - // Shift each element of the unsorted region v[i..] as far left as is needed to make v sorted. - for i in offset..len { - // SAFETY: we tested that `offset` must be at least 1, so this loop is only entered if len - // >= 2. The range is exclusive and we know `i` must be at least 1 so this slice has at - // >least len 2. - unsafe { - insert_tail(&mut v[..=i], is_less); - } - } -} - -/// Sort `v` assuming `v[offset..]` is already sorted. -/// -/// Never inline this function to avoid code bloat. It still optimizes nicely and has practically no -/// performance impact. Even improving performance in some cases. -#[inline(never)] -fn insertion_sort_shift_right(v: &mut [T], offset: usize, is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - let len = v.len(); - - // Using assert here improves performance. - assert!(offset != 0 && offset <= len && len >= 2); - - // Shift each element of the unsorted region v[..i] as far left as is needed to make v sorted. - for i in (0..offset).rev() { - // SAFETY: we tested that `offset` must be at least 1, so this loop is only entered if len - // >= 2.We ensured that the slice length is always at least 2 long. We know that start_found - // will be at least one less than end, and the range is exclusive. Which gives us i always - // <= (end - 2). - unsafe { - insert_head(&mut v[i..len], is_less); - } - } -} - -/// Partially sorts a slice by shifting several out-of-order elements around. -/// -/// Returns `true` if the slice is sorted at the end. This function is *O*(*n*) worst-case. -#[cold] -fn partial_insertion_sort(v: &mut [T], is_less: &mut F) -> bool -where - F: FnMut(&T, &T) -> bool, -{ - // Maximum number of adjacent out-of-order pairs that will get shifted. - const MAX_STEPS: usize = 5; - // If the slice is shorter than this, don't shift any elements. - const SHORTEST_SHIFTING: usize = 50; - - let len = v.len(); - let mut i = 1; - - for _ in 0..MAX_STEPS { - // SAFETY: We already explicitly did the bound checking with `i < len`. - // All our subsequent indexing is only in the range `0 <= index < len` - unsafe { - // Find the next pair of adjacent out-of-order elements. - while i < len && !is_less(v.get_unchecked(i), v.get_unchecked(i - 1)) { - i += 1; - } - } - - // Are we done? - if i == len { - return true; - } - - // Don't shift elements on short arrays, that has a performance cost. - if len < SHORTEST_SHIFTING { - return false; - } - - // Swap the found pair of elements. This puts them in correct order. - v.swap(i - 1, i); - - if i >= 2 { - // Shift the smaller element to the left. - insertion_sort_shift_left(&mut v[..i], i - 1, is_less); - - // Shift the greater element to the right. - insertion_sort_shift_right(&mut v[..i], 1, is_less); - } - } - - // Didn't manage to sort the slice in the limited number of steps. - false -} - -/// Sorts `v` using heapsort, which guarantees *O*(*n* \* log(*n*)) worst-case. -#[cold] -#[unstable(feature = "sort_internals", reason = "internal to sort module", issue = "none")] -pub fn heapsort(v: &mut [T], mut is_less: F) -where - F: FnMut(&T, &T) -> bool, -{ - // This binary heap respects the invariant `parent >= child`. - let mut sift_down = |v: &mut [T], mut node| { - loop { - // Children of `node`. - let mut child = 2 * node + 1; - if child >= v.len() { - break; - } - - // Choose the greater child. - if child + 1 < v.len() { - // We need a branch to be sure not to out-of-bounds index, - // but it's highly predictable. The comparison, however, - // is better done branchless, especially for primitives. - child += is_less(&v[child], &v[child + 1]) as usize; - } - - // Stop if the invariant holds at `node`. - if !is_less(&v[node], &v[child]) { - break; - } - - // Swap `node` with the greater child, move one step down, and continue sifting. - v.swap(node, child); - node = child; - } - }; - - // Build the heap in linear time. - for i in (0..v.len() / 2).rev() { - sift_down(v, i); - } - - // Pop maximal elements from the heap. - for i in (1..v.len()).rev() { - v.swap(0, i); - sift_down(&mut v[..i], 0); - } -} - -/// Partitions `v` into elements smaller than `pivot`, followed by elements greater than or equal -/// to `pivot`. -/// -/// Returns the number of elements smaller than `pivot`. -/// -/// Partitioning is performed block-by-block in order to minimize the cost of branching operations. -/// This idea is presented in the [BlockQuicksort][pdf] paper. -/// -/// [pdf]: https://drops.dagstuhl.de/opus/volltexte/2016/6389/pdf/LIPIcs-ESA-2016-38.pdf -fn partition_in_blocks(v: &mut [T], pivot: &T, is_less: &mut F) -> usize -where - F: FnMut(&T, &T) -> bool, -{ - // Number of elements in a typical block. - const BLOCK: usize = 128; - - // The partitioning algorithm repeats the following steps until completion: - // - // 1. Trace a block from the left side to identify elements greater than or equal to the pivot. - // 2. Trace a block from the right side to identify elements smaller than the pivot. - // 3. Exchange the identified elements between the left and right side. - // - // We keep the following variables for a block of elements: - // - // 1. `block` - Number of elements in the block. - // 2. `start` - Start pointer into the `offsets` array. - // 3. `end` - End pointer into the `offsets` array. - // 4. `offsets` - Indices of out-of-order elements within the block. - - // The current block on the left side (from `l` to `l.add(block_l)`). - let mut l = v.as_mut_ptr(); - let mut block_l = BLOCK; - let mut start_l = ptr::null_mut(); - let mut end_l = ptr::null_mut(); - let mut offsets_l = [MaybeUninit::::uninit(); BLOCK]; - - // The current block on the right side (from `r.sub(block_r)` to `r`). - // SAFETY: The documentation for .add() specifically mention that `vec.as_ptr().add(vec.len())` is always safe - let mut r = unsafe { l.add(v.len()) }; - let mut block_r = BLOCK; - let mut start_r = ptr::null_mut(); - let mut end_r = ptr::null_mut(); - let mut offsets_r = [MaybeUninit::::uninit(); BLOCK]; - - // FIXME: When we get VLAs, try creating one array of length `min(v.len(), 2 * BLOCK)` rather - // than two fixed-size arrays of length `BLOCK`. VLAs might be more cache-efficient. - - // Returns the number of elements between pointers `l` (inclusive) and `r` (exclusive). - fn width(l: *mut T, r: *mut T) -> usize { - assert!(mem::size_of::() > 0); - // FIXME: this should *likely* use `offset_from`, but more - // investigation is needed (including running tests in miri). - (r.addr() - l.addr()) / mem::size_of::() - } - - loop { - // We are done with partitioning block-by-block when `l` and `r` get very close. Then we do - // some patch-up work in order to partition the remaining elements in between. - let is_done = width(l, r) <= 2 * BLOCK; - - if is_done { - // Number of remaining elements (still not compared to the pivot). - let mut rem = width(l, r); - if start_l < end_l || start_r < end_r { - rem -= BLOCK; - } - - // Adjust block sizes so that the left and right block don't overlap, but get perfectly - // aligned to cover the whole remaining gap. - if start_l < end_l { - block_r = rem; - } else if start_r < end_r { - block_l = rem; - } else { - // There were the same number of elements to switch on both blocks during the last - // iteration, so there are no remaining elements on either block. Cover the remaining - // items with roughly equally-sized blocks. - block_l = rem / 2; - block_r = rem - block_l; - } - debug_assert!(block_l <= BLOCK && block_r <= BLOCK); - debug_assert!(width(l, r) == block_l + block_r); - } - - if start_l == end_l { - // Trace `block_l` elements from the left side. - start_l = MaybeUninit::slice_as_mut_ptr(&mut offsets_l); - end_l = start_l; - let mut elem = l; - - for i in 0..block_l { - // SAFETY: The unsafety operations below involve the usage of the `offset`. - // According to the conditions required by the function, we satisfy them because: - // 1. `offsets_l` is stack-allocated, and thus considered separate allocated object. - // 2. The function `is_less` returns a `bool`. - // Casting a `bool` will never overflow `isize`. - // 3. We have guaranteed that `block_l` will be `<= BLOCK`. - // Plus, `end_l` was initially set to the begin pointer of `offsets_` which was declared on the stack. - // Thus, we know that even in the worst case (all invocations of `is_less` returns false) we will only be at most 1 byte pass the end. - // Another unsafety operation here is dereferencing `elem`. - // However, `elem` was initially the begin pointer to the slice which is always valid. - unsafe { - // Branchless comparison. - *end_l = i as u8; - end_l = end_l.add(!is_less(&*elem, pivot) as usize); - elem = elem.add(1); - } - } - } - - if start_r == end_r { - // Trace `block_r` elements from the right side. - start_r = MaybeUninit::slice_as_mut_ptr(&mut offsets_r); - end_r = start_r; - let mut elem = r; - - for i in 0..block_r { - // SAFETY: The unsafety operations below involve the usage of the `offset`. - // According to the conditions required by the function, we satisfy them because: - // 1. `offsets_r` is stack-allocated, and thus considered separate allocated object. - // 2. The function `is_less` returns a `bool`. - // Casting a `bool` will never overflow `isize`. - // 3. We have guaranteed that `block_r` will be `<= BLOCK`. - // Plus, `end_r` was initially set to the begin pointer of `offsets_` which was declared on the stack. - // Thus, we know that even in the worst case (all invocations of `is_less` returns true) we will only be at most 1 byte pass the end. - // Another unsafety operation here is dereferencing `elem`. - // However, `elem` was initially `1 * sizeof(T)` past the end and we decrement it by `1 * sizeof(T)` before accessing it. - // Plus, `block_r` was asserted to be less than `BLOCK` and `elem` will therefore at most be pointing to the beginning of the slice. - unsafe { - // Branchless comparison. - elem = elem.sub(1); - *end_r = i as u8; - end_r = end_r.add(is_less(&*elem, pivot) as usize); - } - } - } - - // Number of out-of-order elements to swap between the left and right side. - let count = cmp::min(width(start_l, end_l), width(start_r, end_r)); - - if count > 0 { - macro_rules! left { - () => { - l.add(usize::from(*start_l)) - }; - } - macro_rules! right { - () => { - r.sub(usize::from(*start_r) + 1) - }; - } - - // Instead of swapping one pair at the time, it is more efficient to perform a cyclic - // permutation. This is not strictly equivalent to swapping, but produces a similar - // result using fewer memory operations. - - // SAFETY: The use of `ptr::read` is valid because there is at least one element in - // both `offsets_l` and `offsets_r`, so `left!` is a valid pointer to read from. - // - // The uses of `left!` involve calls to `offset` on `l`, which points to the - // beginning of `v`. All the offsets pointed-to by `start_l` are at most `block_l`, so - // these `offset` calls are safe as all reads are within the block. The same argument - // applies for the uses of `right!`. - // - // The calls to `start_l.offset` are valid because there are at most `count-1` of them, - // plus the final one at the end of the unsafe block, where `count` is the minimum number - // of collected offsets in `offsets_l` and `offsets_r`, so there is no risk of there not - // being enough elements. The same reasoning applies to the calls to `start_r.offset`. - // - // The calls to `copy_nonoverlapping` are safe because `left!` and `right!` are guaranteed - // not to overlap, and are valid because of the reasoning above. - unsafe { - let tmp = ptr::read(left!()); - ptr::copy_nonoverlapping(right!(), left!(), 1); - - for _ in 1..count { - start_l = start_l.add(1); - ptr::copy_nonoverlapping(left!(), right!(), 1); - start_r = start_r.add(1); - ptr::copy_nonoverlapping(right!(), left!(), 1); - } - - ptr::copy_nonoverlapping(&tmp, right!(), 1); - mem::forget(tmp); - start_l = start_l.add(1); - start_r = start_r.add(1); - } - } - - if start_l == end_l { - // All out-of-order elements in the left block were moved. Move to the next block. - - // block-width-guarantee - // SAFETY: if `!is_done` then the slice width is guaranteed to be at least `2*BLOCK` wide. There - // are at most `BLOCK` elements in `offsets_l` because of its size, so the `offset` operation is - // safe. Otherwise, the debug assertions in the `is_done` case guarantee that - // `width(l, r) == block_l + block_r`, namely, that the block sizes have been adjusted to account - // for the smaller number of remaining elements. - l = unsafe { l.add(block_l) }; - } - - if start_r == end_r { - // All out-of-order elements in the right block were moved. Move to the previous block. - - // SAFETY: Same argument as [block-width-guarantee]. Either this is a full block `2*BLOCK`-wide, - // or `block_r` has been adjusted for the last handful of elements. - r = unsafe { r.sub(block_r) }; - } - - if is_done { - break; - } - } - - // All that remains now is at most one block (either the left or the right) with out-of-order - // elements that need to be moved. Such remaining elements can be simply shifted to the end - // within their block. - - if start_l < end_l { - // The left block remains. - // Move its remaining out-of-order elements to the far right. - debug_assert_eq!(width(l, r), block_l); - while start_l < end_l { - // remaining-elements-safety - // SAFETY: while the loop condition holds there are still elements in `offsets_l`, so it - // is safe to point `end_l` to the previous element. - // - // The `ptr::swap` is safe if both its arguments are valid for reads and writes: - // - Per the debug assert above, the distance between `l` and `r` is `block_l` - // elements, so there can be at most `block_l` remaining offsets between `start_l` - // and `end_l`. This means `r` will be moved at most `block_l` steps back, which - // makes the `r.offset` calls valid (at that point `l == r`). - // - `offsets_l` contains valid offsets into `v` collected during the partitioning of - // the last block, so the `l.offset` calls are valid. - unsafe { - end_l = end_l.sub(1); - ptr::swap(l.add(usize::from(*end_l)), r.sub(1)); - r = r.sub(1); - } - } - width(v.as_mut_ptr(), r) - } else if start_r < end_r { - // The right block remains. - // Move its remaining out-of-order elements to the far left. - debug_assert_eq!(width(l, r), block_r); - while start_r < end_r { - // SAFETY: See the reasoning in [remaining-elements-safety]. - unsafe { - end_r = end_r.sub(1); - ptr::swap(l, r.sub(usize::from(*end_r) + 1)); - l = l.add(1); - } - } - width(v.as_mut_ptr(), l) - } else { - // Nothing else to do, we're done. - width(v.as_mut_ptr(), l) - } -} - -/// Partitions `v` into elements smaller than `v[pivot]`, followed by elements greater than or -/// equal to `v[pivot]`. -/// -/// Returns a tuple of: -/// -/// 1. Number of elements smaller than `v[pivot]`. -/// 2. True if `v` was already partitioned. -pub(super) fn partition(v: &mut [T], pivot: usize, is_less: &mut F) -> (usize, bool) -where - F: FnMut(&T, &T) -> bool, -{ - let (mid, was_partitioned) = { - // Place the pivot at the beginning of slice. - v.swap(0, pivot); - let (pivot, v) = v.split_at_mut(1); - let pivot = &mut pivot[0]; - - // Read the pivot into a stack-allocated variable for efficiency. If a following comparison - // operation panics, the pivot will be automatically written back into the slice. - - // SAFETY: `pivot` is a reference to the first element of `v`, so `ptr::read` is safe. - let tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) }); - let _pivot_guard = InsertionHole { src: &*tmp, dest: pivot }; - let pivot = &*tmp; - - // Find the first pair of out-of-order elements. - let mut l = 0; - let mut r = v.len(); - - // SAFETY: The unsafety below involves indexing an array. - // For the first one: We already do the bounds checking here with `l < r`. - // For the second one: We initially have `l == 0` and `r == v.len()` and we checked that `l < r` at every indexing operation. - // From here we know that `r` must be at least `r == l` which was shown to be valid from the first one. - unsafe { - // Find the first element greater than or equal to the pivot. - while l < r && is_less(v.get_unchecked(l), pivot) { - l += 1; - } - - // Find the last element smaller that the pivot. - while l < r && !is_less(v.get_unchecked(r - 1), pivot) { - r -= 1; - } - } - - (l + partition_in_blocks(&mut v[l..r], pivot, is_less), l >= r) - - // `_pivot_guard` goes out of scope and writes the pivot (which is a stack-allocated - // variable) back into the slice where it originally was. This step is critical in ensuring - // safety! - }; - - // Place the pivot between the two partitions. - v.swap(0, mid); - - (mid, was_partitioned) -} - -/// Partitions `v` into elements equal to `v[pivot]` followed by elements greater than `v[pivot]`. -/// -/// Returns the number of elements equal to the pivot. It is assumed that `v` does not contain -/// elements smaller than the pivot. -pub(super) fn partition_equal(v: &mut [T], pivot: usize, is_less: &mut F) -> usize -where - F: FnMut(&T, &T) -> bool, -{ - // Place the pivot at the beginning of slice. - v.swap(0, pivot); - let (pivot, v) = v.split_at_mut(1); - let pivot = &mut pivot[0]; - - // Read the pivot into a stack-allocated variable for efficiency. If a following comparison - // operation panics, the pivot will be automatically written back into the slice. - // SAFETY: The pointer here is valid because it is obtained from a reference to a slice. - let tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) }); - let _pivot_guard = InsertionHole { src: &*tmp, dest: pivot }; - let pivot = &*tmp; - - let len = v.len(); - if len == 0 { - return 0; - } - - // Now partition the slice. - let mut l = 0; - let mut r = len; - loop { - // SAFETY: The unsafety below involves indexing an array. - // For the first one: We already do the bounds checking here with `l < r`. - // For the second one: We initially have `l == 0` and `r == v.len()` and we checked that `l < r` at every indexing operation. - // From here we know that `r` must be at least `r == l` which was shown to be valid from the first one. - unsafe { - // Find the first element greater than the pivot. - while l < r && !is_less(pivot, v.get_unchecked(l)) { - l += 1; - } - - // Find the last element equal to the pivot. - loop { - r -= 1; - if l >= r || !is_less(pivot, v.get_unchecked(r)) { - break; - } - } - - // Are we done? - if l >= r { - break; - } - - // Swap the found pair of out-of-order elements. - let ptr = v.as_mut_ptr(); - ptr::swap(ptr.add(l), ptr.add(r)); - l += 1; - } - } - - // We found `l` elements equal to the pivot. Add 1 to account for the pivot itself. - l + 1 - - // `_pivot_guard` goes out of scope and writes the pivot (which is a stack-allocated variable) - // back into the slice where it originally was. This step is critical in ensuring safety! -} - -/// Scatters some elements around in an attempt to break patterns that might cause imbalanced -/// partitions in quicksort. -#[cold] -pub(super) fn break_patterns(v: &mut [T]) { - let len = v.len(); - if len >= 8 { - let mut seed = len; - let mut gen_usize = || { - // Pseudorandom number generator from the "Xorshift RNGs" paper by George Marsaglia. - if usize::BITS <= 32 { - let mut r = seed as u32; - r ^= r << 13; - r ^= r >> 17; - r ^= r << 5; - seed = r as usize; - seed - } else { - let mut r = seed as u64; - r ^= r << 13; - r ^= r >> 7; - r ^= r << 17; - seed = r as usize; - seed - } - }; - - // Take random numbers modulo this number. - // The number fits into `usize` because `len` is not greater than `isize::MAX`. - let modulus = len.next_power_of_two(); - - // Some pivot candidates will be in the nearby of this index. Let's randomize them. - let pos = len / 4 * 2; - - for i in 0..3 { - // Generate a random number modulo `len`. However, in order to avoid costly operations - // we first take it modulo a power of two, and then decrease by `len` until it fits - // into the range `[0, len - 1]`. - let mut other = gen_usize() & (modulus - 1); - - // `other` is guaranteed to be less than `2 * len`. - if other >= len { - other -= len; - } - - v.swap(pos - 1 + i, other); - } - } -} - -/// Chooses a pivot in `v` and returns the index and `true` if the slice is likely already sorted. -/// -/// Elements in `v` might be reordered in the process. -pub(super) fn choose_pivot(v: &mut [T], is_less: &mut F) -> (usize, bool) -where - F: FnMut(&T, &T) -> bool, -{ - // Minimum length to choose the median-of-medians method. - // Shorter slices use the simple median-of-three method. - const SHORTEST_MEDIAN_OF_MEDIANS: usize = 50; - // Maximum number of swaps that can be performed in this function. - const MAX_SWAPS: usize = 4 * 3; - - let len = v.len(); - - // Three indices near which we are going to choose a pivot. - let mut a = len / 4 * 1; - let mut b = len / 4 * 2; - let mut c = len / 4 * 3; - - // Counts the total number of swaps we are about to perform while sorting indices. - let mut swaps = 0; - - if len >= 8 { - // Swaps indices so that `v[a] <= v[b]`. - // SAFETY: `len >= 8` so there are at least two elements in the neighborhoods of - // `a`, `b` and `c`. This means the three calls to `sort_adjacent` result in - // corresponding calls to `sort3` with valid 3-item neighborhoods around each - // pointer, which in turn means the calls to `sort2` are done with valid - // references. Thus the `v.get_unchecked` calls are safe, as is the `ptr::swap` - // call. - let mut sort2 = |a: &mut usize, b: &mut usize| unsafe { - if is_less(v.get_unchecked(*b), v.get_unchecked(*a)) { - ptr::swap(a, b); - swaps += 1; - } - }; - - // Swaps indices so that `v[a] <= v[b] <= v[c]`. - let mut sort3 = |a: &mut usize, b: &mut usize, c: &mut usize| { - sort2(a, b); - sort2(b, c); - sort2(a, b); - }; - - if len >= SHORTEST_MEDIAN_OF_MEDIANS { - // Finds the median of `v[a - 1], v[a], v[a + 1]` and stores the index into `a`. - let mut sort_adjacent = |a: &mut usize| { - let tmp = *a; - sort3(&mut (tmp - 1), a, &mut (tmp + 1)); - }; - - // Find medians in the neighborhoods of `a`, `b`, and `c`. - sort_adjacent(&mut a); - sort_adjacent(&mut b); - sort_adjacent(&mut c); - } - - // Find the median among `a`, `b`, and `c`. - sort3(&mut a, &mut b, &mut c); - } - - if swaps < MAX_SWAPS { - (b, swaps == 0) - } else { - // The maximum number of swaps was performed. Chances are the slice is descending or mostly - // descending, so reversing will probably help sort it faster. - v.reverse(); - (len - 1 - b, true) - } -} - -/// Sorts `v` recursively. -/// -/// If the slice had a predecessor in the original array, it is specified as `pred`. -/// -/// `limit` is the number of allowed imbalanced partitions before switching to `heapsort`. If zero, -/// this function will immediately switch to heapsort. -fn recurse<'a, T, F>(mut v: &'a mut [T], is_less: &mut F, mut pred: Option<&'a T>, mut limit: u32) -where - F: FnMut(&T, &T) -> bool, -{ - // Slices of up to this length get sorted using insertion sort. - const MAX_INSERTION: usize = 20; - - // True if the last partitioning was reasonably balanced. - let mut was_balanced = true; - // True if the last partitioning didn't shuffle elements (the slice was already partitioned). - let mut was_partitioned = true; - - loop { - let len = v.len(); - - // Very short slices get sorted using insertion sort. - if len <= MAX_INSERTION { - if len >= 2 { - insertion_sort_shift_left(v, 1, is_less); - } - return; - } - - // If too many bad pivot choices were made, simply fall back to heapsort in order to - // guarantee `O(n * log(n))` worst-case. - if limit == 0 { - heapsort(v, is_less); - return; - } - - // If the last partitioning was imbalanced, try breaking patterns in the slice by shuffling - // some elements around. Hopefully we'll choose a better pivot this time. - if !was_balanced { - break_patterns(v); - limit -= 1; - } - - // Choose a pivot and try guessing whether the slice is already sorted. - let (pivot, likely_sorted) = choose_pivot(v, is_less); - - // If the last partitioning was decently balanced and didn't shuffle elements, and if pivot - // selection predicts the slice is likely already sorted... - if was_balanced && was_partitioned && likely_sorted { - // Try identifying several out-of-order elements and shifting them to correct - // positions. If the slice ends up being completely sorted, we're done. - if partial_insertion_sort(v, is_less) { - return; - } - } - - // If the chosen pivot is equal to the predecessor, then it's the smallest element in the - // slice. Partition the slice into elements equal to and elements greater than the pivot. - // This case is usually hit when the slice contains many duplicate elements. - if let Some(p) = pred { - if !is_less(p, &v[pivot]) { - let mid = partition_equal(v, pivot, is_less); - - // Continue sorting elements greater than the pivot. - v = &mut v[mid..]; - continue; - } - } - - // Partition the slice. - let (mid, was_p) = partition(v, pivot, is_less); - was_balanced = cmp::min(mid, len - mid) >= len / 8; - was_partitioned = was_p; - - // Split the slice into `left`, `pivot`, and `right`. - let (left, right) = v.split_at_mut(mid); - let (pivot, right) = right.split_at_mut(1); - let pivot = &pivot[0]; - - // Recurse into the shorter side only in order to minimize the total number of recursive - // calls and consume less stack space. Then just continue with the longer side (this is - // akin to tail recursion). - if left.len() < right.len() { - recurse(left, is_less, pred, limit); - v = right; - pred = Some(pivot); - } else { - recurse(right, is_less, Some(pivot), limit); - v = left; - } - } -} - -/// Sorts `v` using pattern-defeating quicksort, which is *O*(*n* \* log(*n*)) worst-case. -pub fn quicksort(v: &mut [T], mut is_less: F) -where - F: FnMut(&T, &T) -> bool, -{ - // Sorting has no meaningful behavior on zero-sized types. - if T::IS_ZST { - return; - } - - // Limit the number of imbalanced partitions to `floor(log2(len)) + 1`. - let limit = usize::BITS - v.len().leading_zeros(); - - recurse(v, &mut is_less, None, limit); -} - -/// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and -/// stores the result into `v[..]`. -/// -/// # Safety -/// -/// The two slices must be non-empty and `mid` must be in bounds. Buffer `buf` must be long enough -/// to hold a copy of the shorter slice. Also, `T` must not be a zero-sized type. -unsafe fn merge(v: &mut [T], mid: usize, buf: *mut T, is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - let len = v.len(); - let v = v.as_mut_ptr(); - - // SAFETY: mid and len must be in-bounds of v. - let (v_mid, v_end) = unsafe { (v.add(mid), v.add(len)) }; - - // The merge process first copies the shorter run into `buf`. Then it traces the newly copied - // run and the longer run forwards (or backwards), comparing their next unconsumed elements and - // copying the lesser (or greater) one into `v`. - // - // As soon as the shorter run is fully consumed, the process is done. If the longer run gets - // consumed first, then we must copy whatever is left of the shorter run into the remaining - // hole in `v`. - // - // Intermediate state of the process is always tracked by `hole`, which serves two purposes: - // 1. Protects integrity of `v` from panics in `is_less`. - // 2. Fills the remaining hole in `v` if the longer run gets consumed first. - // - // Panic safety: - // - // If `is_less` panics at any point during the process, `hole` will get dropped and fill the - // hole in `v` with the unconsumed range in `buf`, thus ensuring that `v` still holds every - // object it initially held exactly once. - let mut hole; - - if mid <= len - mid { - // The left run is shorter. - - // SAFETY: buf must have enough capacity for `v[..mid]`. - unsafe { - ptr::copy_nonoverlapping(v, buf, mid); - hole = MergeHole { start: buf, end: buf.add(mid), dest: v }; - } - - // Initially, these pointers point to the beginnings of their arrays. - let left = &mut hole.start; - let mut right = v_mid; - let out = &mut hole.dest; - - while *left < hole.end && right < v_end { - // Consume the lesser side. - // If equal, prefer the left run to maintain stability. - - // SAFETY: left and right must be valid and part of v same for out. - unsafe { - let is_l = is_less(&*right, &**left); - let to_copy = if is_l { right } else { *left }; - ptr::copy_nonoverlapping(to_copy, *out, 1); - *out = out.add(1); - right = right.add(is_l as usize); - *left = left.add(!is_l as usize); - } - } - } else { - // The right run is shorter. - - // SAFETY: buf must have enough capacity for `v[mid..]`. - unsafe { - ptr::copy_nonoverlapping(v_mid, buf, len - mid); - hole = MergeHole { start: buf, end: buf.add(len - mid), dest: v_mid }; - } - - // Initially, these pointers point past the ends of their arrays. - let left = &mut hole.dest; - let right = &mut hole.end; - let mut out = v_end; - - while v < *left && buf < *right { - // Consume the greater side. - // If equal, prefer the right run to maintain stability. - - // SAFETY: left and right must be valid and part of v same for out. - unsafe { - let is_l = is_less(&*right.sub(1), &*left.sub(1)); - *left = left.sub(is_l as usize); - *right = right.sub(!is_l as usize); - let to_copy = if is_l { *left } else { *right }; - out = out.sub(1); - ptr::copy_nonoverlapping(to_copy, out, 1); - } - } - } - // Finally, `hole` gets dropped. If the shorter run was not fully consumed, whatever remains of - // it will now be copied into the hole in `v`. - - // When dropped, copies the range `start..end` into `dest..`. - struct MergeHole { - start: *mut T, - end: *mut T, - dest: *mut T, - } - - impl Drop for MergeHole { - fn drop(&mut self) { - // SAFETY: `T` is not a zero-sized type, and these are pointers into a slice's elements. - unsafe { - let len = self.end.sub_ptr(self.start); - ptr::copy_nonoverlapping(self.start, self.dest, len); - } - } - } -} - -/// This merge sort borrows some (but not all) ideas from TimSort, which used to be described in -/// detail [here](https://github.com/python/cpython/blob/main/Objects/listsort.txt). However Python -/// has switched to a Powersort based implementation. -/// -/// The algorithm identifies strictly descending and non-descending subsequences, which are called -/// natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed -/// onto the stack, and then some pairs of adjacent runs are merged until these two invariants are -/// satisfied: -/// -/// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len` -/// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len` -/// -/// The invariants ensure that the total running time is *O*(*n* \* log(*n*)) worst-case. -pub fn merge_sort( - v: &mut [T], - is_less: &mut CmpF, - elem_alloc_fn: ElemAllocF, - elem_dealloc_fn: ElemDeallocF, - run_alloc_fn: RunAllocF, - run_dealloc_fn: RunDeallocF, -) where - CmpF: FnMut(&T, &T) -> bool, - ElemAllocF: Fn(usize) -> *mut T, - ElemDeallocF: Fn(*mut T, usize), - RunAllocF: Fn(usize) -> *mut TimSortRun, - RunDeallocF: Fn(*mut TimSortRun, usize), -{ - // Slices of up to this length get sorted using insertion sort. - const MAX_INSERTION: usize = 20; - - // The caller should have already checked that. - debug_assert!(!T::IS_ZST); - - let len = v.len(); - - // Short arrays get sorted in-place via insertion sort to avoid allocations. - if len <= MAX_INSERTION { - if len >= 2 { - insertion_sort_shift_left(v, 1, is_less); - } - return; - } - - // Allocate a buffer to use as scratch memory. We keep the length 0 so we can keep in it - // shallow copies of the contents of `v` without risking the dtors running on copies if - // `is_less` panics. When merging two sorted runs, this buffer holds a copy of the shorter run, - // which will always have length at most `len / 2`. - let buf = BufGuard::new(len / 2, elem_alloc_fn, elem_dealloc_fn); - let buf_ptr = buf.buf_ptr.as_ptr(); - - let mut runs = RunVec::new(run_alloc_fn, run_dealloc_fn); - - let mut end = 0; - let mut start = 0; - - // Scan forward. Memory pre-fetching prefers forward scanning vs backwards scanning, and the - // code-gen is usually better. For the most sensitive types such as integers, these are merged - // bidirectionally at once. So there is no benefit in scanning backwards. - while end < len { - let (streak_end, was_reversed) = find_streak(&v[start..], is_less); - end += streak_end; - if was_reversed { - v[start..end].reverse(); - } - - // Insert some more elements into the run if it's too short. Insertion sort is faster than - // merge sort on short sequences, so this significantly improves performance. - end = provide_sorted_batch(v, start, end, is_less); - - // Push this run onto the stack. - runs.push(TimSortRun { start, len: end - start }); - start = end; - - // Merge some pairs of adjacent runs to satisfy the invariants. - while let Some(r) = collapse(runs.as_slice(), len) { - let left = runs[r]; - let right = runs[r + 1]; - let merge_slice = &mut v[left.start..right.start + right.len]; - // SAFETY: `buf_ptr` must hold enough capacity for the shorter of the two sides, and - // neither side may be on length 0. - unsafe { - merge(merge_slice, left.len, buf_ptr, is_less); - } - runs[r + 1] = TimSortRun { start: left.start, len: left.len + right.len }; - runs.remove(r); - } - } - - // Finally, exactly one run must remain in the stack. - debug_assert!(runs.len() == 1 && runs[0].start == 0 && runs[0].len == len); - - // Examines the stack of runs and identifies the next pair of runs to merge. More specifically, - // if `Some(r)` is returned, that means `runs[r]` and `runs[r + 1]` must be merged next. If the - // algorithm should continue building a new run instead, `None` is returned. - // - // TimSort is infamous for its buggy implementations, as described here: - // http://envisage-project.eu/timsort-specification-and-verification/ - // - // The gist of the story is: we must enforce the invariants on the top four runs on the stack. - // Enforcing them on just top three is not sufficient to ensure that the invariants will still - // hold for *all* runs in the stack. - // - // This function correctly checks invariants for the top four runs. Additionally, if the top - // run starts at index 0, it will always demand a merge operation until the stack is fully - // collapsed, in order to complete the sort. - #[inline] - fn collapse(runs: &[TimSortRun], stop: usize) -> Option { - let n = runs.len(); - if n >= 2 - && (runs[n - 1].start + runs[n - 1].len == stop - || runs[n - 2].len <= runs[n - 1].len - || (n >= 3 && runs[n - 3].len <= runs[n - 2].len + runs[n - 1].len) - || (n >= 4 && runs[n - 4].len <= runs[n - 3].len + runs[n - 2].len)) - { - if n >= 3 && runs[n - 3].len < runs[n - 1].len { Some(n - 3) } else { Some(n - 2) } - } else { - None - } - } - - // Extremely basic versions of Vec. - // Their use is super limited and by having the code here, it allows reuse between the sort - // implementations. - struct BufGuard - where - ElemDeallocF: Fn(*mut T, usize), - { - buf_ptr: ptr::NonNull, - capacity: usize, - elem_dealloc_fn: ElemDeallocF, - } - - impl BufGuard - where - ElemDeallocF: Fn(*mut T, usize), - { - fn new( - len: usize, - elem_alloc_fn: ElemAllocF, - elem_dealloc_fn: ElemDeallocF, - ) -> Self - where - ElemAllocF: Fn(usize) -> *mut T, - { - Self { - buf_ptr: ptr::NonNull::new(elem_alloc_fn(len)).unwrap(), - capacity: len, - elem_dealloc_fn, - } - } - } - - impl Drop for BufGuard - where - ElemDeallocF: Fn(*mut T, usize), - { - fn drop(&mut self) { - (self.elem_dealloc_fn)(self.buf_ptr.as_ptr(), self.capacity); - } - } - - struct RunVec - where - RunAllocF: Fn(usize) -> *mut TimSortRun, - RunDeallocF: Fn(*mut TimSortRun, usize), - { - buf_ptr: ptr::NonNull, - capacity: usize, - len: usize, - run_alloc_fn: RunAllocF, - run_dealloc_fn: RunDeallocF, - } - - impl RunVec - where - RunAllocF: Fn(usize) -> *mut TimSortRun, - RunDeallocF: Fn(*mut TimSortRun, usize), - { - fn new(run_alloc_fn: RunAllocF, run_dealloc_fn: RunDeallocF) -> Self { - // Most slices can be sorted with at most 16 runs in-flight. - const START_RUN_CAPACITY: usize = 16; - - Self { - buf_ptr: ptr::NonNull::new(run_alloc_fn(START_RUN_CAPACITY)).unwrap(), - capacity: START_RUN_CAPACITY, - len: 0, - run_alloc_fn, - run_dealloc_fn, - } - } - - fn push(&mut self, val: TimSortRun) { - if self.len == self.capacity { - let old_capacity = self.capacity; - let old_buf_ptr = self.buf_ptr.as_ptr(); - - self.capacity = self.capacity * 2; - self.buf_ptr = ptr::NonNull::new((self.run_alloc_fn)(self.capacity)).unwrap(); - - // SAFETY: buf_ptr new and old were correctly allocated and old_buf_ptr has - // old_capacity valid elements. - unsafe { - ptr::copy_nonoverlapping(old_buf_ptr, self.buf_ptr.as_ptr(), old_capacity); - } - - (self.run_dealloc_fn)(old_buf_ptr, old_capacity); - } - - // SAFETY: The invariant was just checked. - unsafe { - self.buf_ptr.as_ptr().add(self.len).write(val); - } - self.len += 1; - } - - fn remove(&mut self, index: usize) { - if index >= self.len { - panic!("Index out of bounds"); - } - - // SAFETY: buf_ptr needs to be valid and len invariant upheld. - unsafe { - // the place we are taking from. - let ptr = self.buf_ptr.as_ptr().add(index); - - // Shift everything down to fill in that spot. - ptr::copy(ptr.add(1), ptr, self.len - index - 1); - } - self.len -= 1; - } - - fn as_slice(&self) -> &[TimSortRun] { - // SAFETY: Safe as long as buf_ptr is valid and len invariant was upheld. - unsafe { &*ptr::slice_from_raw_parts(self.buf_ptr.as_ptr(), self.len) } - } - - fn len(&self) -> usize { - self.len - } - } - - impl core::ops::Index for RunVec - where - RunAllocF: Fn(usize) -> *mut TimSortRun, - RunDeallocF: Fn(*mut TimSortRun, usize), - { - type Output = TimSortRun; - - fn index(&self, index: usize) -> &Self::Output { - if index < self.len { - // SAFETY: buf_ptr and len invariant must be upheld. - unsafe { - return &*(self.buf_ptr.as_ptr().add(index)); - } - } - - panic!("Index out of bounds"); - } - } - - impl core::ops::IndexMut for RunVec - where - RunAllocF: Fn(usize) -> *mut TimSortRun, - RunDeallocF: Fn(*mut TimSortRun, usize), - { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - if index < self.len { - // SAFETY: buf_ptr and len invariant must be upheld. - unsafe { - return &mut *(self.buf_ptr.as_ptr().add(index)); - } - } - - panic!("Index out of bounds"); - } - } - - impl Drop for RunVec - where - RunAllocF: Fn(usize) -> *mut TimSortRun, - RunDeallocF: Fn(*mut TimSortRun, usize), - { - fn drop(&mut self) { - // As long as TimSortRun is Copy we don't need to drop them individually but just the - // whole allocation. - (self.run_dealloc_fn)(self.buf_ptr.as_ptr(), self.capacity); - } - } -} - -/// Internal type used by merge_sort. -#[derive(Clone, Copy, Debug)] -pub struct TimSortRun { - len: usize, - start: usize, -} - -/// Takes a range as denoted by start and end, that is already sorted and extends it to the right if -/// necessary with sorts optimized for smaller ranges such as insertion sort. -fn provide_sorted_batch(v: &mut [T], start: usize, mut end: usize, is_less: &mut F) -> usize -where - F: FnMut(&T, &T) -> bool, -{ - let len = v.len(); - assert!(end >= start && end <= len); - - // This value is a balance between least comparisons and best performance, as - // influenced by for example cache locality. - const MIN_INSERTION_RUN: usize = 10; - - // Insert some more elements into the run if it's too short. Insertion sort is faster than - // merge sort on short sequences, so this significantly improves performance. - let start_end_diff = end - start; - - if start_end_diff < MIN_INSERTION_RUN && end < len { - // v[start_found..end] are elements that are already sorted in the input. We want to extend - // the sorted region to the left, so we push up MIN_INSERTION_RUN - 1 to the right. Which is - // more efficient that trying to push those already sorted elements to the left. - end = cmp::min(start + MIN_INSERTION_RUN, len); - let presorted_start = cmp::max(start_end_diff, 1); - - insertion_sort_shift_left(&mut v[start..end], presorted_start, is_less); - } - - end -} - -/// Finds a streak of presorted elements starting at the beginning of the slice. Returns the first -/// value that is not part of said streak, and a bool denoting whether the streak was reversed. -/// Streaks can be increasing or decreasing. -fn find_streak(v: &[T], is_less: &mut F) -> (usize, bool) -where - F: FnMut(&T, &T) -> bool, -{ - let len = v.len(); - - if len < 2 { - return (len, false); - } - - let mut end = 2; - - // SAFETY: See below specific. - unsafe { - // SAFETY: We checked that len >= 2, so 0 and 1 are valid indices. - let assume_reverse = is_less(v.get_unchecked(1), v.get_unchecked(0)); - - // SAFETY: We know end >= 2 and check end < len. - // From that follows that accessing v at end and end - 1 is safe. - if assume_reverse { - while end < len && is_less(v.get_unchecked(end), v.get_unchecked(end - 1)) { - end += 1; - } - - (end, true) - } else { - while end < len && !is_less(v.get_unchecked(end), v.get_unchecked(end - 1)) { - end += 1; - } - (end, false) - } - } -} diff --git a/library/core/src/slice/sort/mod.rs b/library/core/src/slice/sort/mod.rs new file mode 100644 index 0000000000000..79852708b81ea --- /dev/null +++ b/library/core/src/slice/sort/mod.rs @@ -0,0 +1,8 @@ +//! This module and the contained sub-modules contains the code for efficient and robust sort +//! implementations, as well as the domain adjacent implementation of `select_nth_unstable`. + +pub mod stable; +pub mod unstable; + +pub(crate) mod select; +pub(crate) mod shared; diff --git a/library/core/src/slice/select.rs b/library/core/src/slice/sort/select.rs similarity index 75% rename from library/core/src/slice/select.rs rename to library/core/src/slice/sort/select.rs index ffc193578e075..f6529f23bcb3f 100644 --- a/library/core/src/slice/select.rs +++ b/library/core/src/slice/sort/select.rs @@ -1,45 +1,77 @@ -//! Slice selection -//! //! This module contains the implementation for `slice::select_nth_unstable`. -//! It uses an introselect algorithm based on Orson Peters' pattern-defeating quicksort, -//! published at: +//! It uses an introselect algorithm based on ipnsort by Lukas Bergdoll and Orson Peters, +//! published at: //! //! The fallback algorithm used for introselect is Median of Medians using Tukey's Ninther //! for pivot selection. Using this as a fallback ensures O(n) worst case running time with //! better performance than one would get using heapsort as fallback. -use crate::cmp; use crate::mem::{self, SizedTypeProperties}; -use crate::slice::sort::{ - break_patterns, choose_pivot, insertion_sort_shift_left, partition, partition_equal, -}; +use crate::slice::sort::shared::pivot::choose_pivot; +use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; +use crate::slice::sort::unstable::quicksort::partition; + +/// Reorders the slice such that the element at `index` is at its final sorted position. +pub(crate) fn partition_at_index( + v: &mut [T], + index: usize, + mut is_less: F, +) -> (&mut [T], &mut T, &mut [T]) +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); -// For slices of up to this length it's probably faster to simply sort them. -// Defined at the module scope because it's used in multiple functions. -const MAX_INSERTION: usize = 10; + // Puts a lower limit of 1 on `len`. + if index >= len { + panic!("partition_at_index index {} greater than length of slice {}", index, len); + } + + if T::IS_ZST { + // Sorting has no meaningful behavior on zero-sized types. Do nothing. + } else if index == len - 1 { + // Find max element and place it in the last position of the array. We're free to use + // `unwrap()` here because we checked that `v` is not empty. + let max_idx = max_index(v, &mut is_less).unwrap(); + v.swap(max_idx, index); + } else if index == 0 { + // Find min element and place it in the first position of the array. We're free to use + // `unwrap()` here because we checked that `v` is not empty. + let min_idx = min_index(v, &mut is_less).unwrap(); + v.swap(min_idx, index); + } else { + partition_at_index_loop(v, index, None, &mut is_less); + } + + let (left, right) = v.split_at_mut(index); + let (pivot, right) = right.split_at_mut(1); + let pivot = &mut pivot[0]; + (left, pivot, right) +} + +// For small sub-slices it's faster to use a dedicated small-sort, but because it is only called at +// most once, it doesn't make sense to use something more sophisticated than insertion-sort. +const INSERTION_SORT_THRESHOLD: usize = 16; fn partition_at_index_loop<'a, T, F>( mut v: &'a mut [T], mut index: usize, + mut ancestor_pivot: Option<&'a T>, is_less: &mut F, - mut pred: Option<&'a T>, ) where F: FnMut(&T, &T) -> bool, { - // Limit the amount of iterations and fall back to fast deterministic selection - // to ensure O(n) worst case running time. This limit needs to be constant, because - // using `ilog2(len)` like in `sort` would result in O(n log n) time complexity. - // The exact value of the limit is chosen somewhat arbitrarily, but for most inputs bad pivot - // selections should be relatively rare, so the limit usually shouldn't be reached - // anyways. + // Limit the amount of iterations and fall back to fast deterministic selection to ensure O(n) + // worst case running time. This limit needs to be constant, because using `ilog2(len)` like in + // `sort` would result in O(n log n) time complexity. The exact value of the limit is chosen + // somewhat arbitrarily, but for most inputs bad pivot selections should be relatively rare, so + // the limit is reached for sub-slices len / (2^limit or less). Which makes the remaining work + // with the fallback minimal in relative terms. let mut limit = 16; - // True if the last partitioning was reasonably balanced. - let mut was_balanced = true; - loop { - if v.len() <= MAX_INSERTION { - if v.len() > 1 { + if v.len() <= INSERTION_SORT_THRESHOLD { + if v.len() >= 2 { insertion_sort_shift_left(v, 1, is_less); } return; @@ -50,38 +82,38 @@ fn partition_at_index_loop<'a, T, F>( return; } - // If the last partitioning was imbalanced, try breaking patterns in the slice by shuffling - // some elements around. Hopefully we'll choose a better pivot this time. - if !was_balanced { - break_patterns(v); - limit -= 1; - } + limit -= 1; // Choose a pivot - let (pivot, _) = choose_pivot(v, is_less); + let pivot_pos = choose_pivot(v, is_less); // If the chosen pivot is equal to the predecessor, then it's the smallest element in the // slice. Partition the slice into elements equal to and elements greater than the pivot. // This case is usually hit when the slice contains many duplicate elements. - if let Some(p) = pred { - if !is_less(p, &v[pivot]) { - let mid = partition_equal(v, pivot, is_less); + if let Some(p) = ancestor_pivot { + // SAFETY: choose_pivot promises to return a valid pivot position. + let pivot = unsafe { v.get_unchecked(pivot_pos) }; + + if !is_less(p, pivot) { + let num_lt = partition(v, pivot_pos, &mut |a, b| !is_less(b, a)); + + // Continue sorting elements greater than the pivot. We know that `mid` contains + // the pivot. So we can continue after `mid`. + let mid = num_lt + 1; // If we've passed our index, then we're good. if mid > index { return; } - // Otherwise, continue sorting elements greater than the pivot. v = &mut v[mid..]; index = index - mid; - pred = None; + ancestor_pivot = None; continue; } } - let (mid, _) = partition(v, pivot, is_less); - was_balanced = cmp::min(mid, v.len() - mid) >= v.len() / 8; + let mid = partition(v, pivot_pos, is_less); // Split the slice into `left`, `pivot`, and `right`. let (left, right) = v.split_at_mut(mid); @@ -91,7 +123,7 @@ fn partition_at_index_loop<'a, T, F>( if mid < index { v = right; index = index - mid - 1; - pred = Some(pivot); + ancestor_pivot = Some(pivot); } else if mid > index { v = left; } else { @@ -122,41 +154,6 @@ fn max_index bool>(slice: &[T], is_less: &mut F) -> Optio .map(|(i, _)| i) } -/// Reorder the slice such that the element at `index` is at its final sorted position. -pub fn partition_at_index( - v: &mut [T], - index: usize, - mut is_less: F, -) -> (&mut [T], &mut T, &mut [T]) -where - F: FnMut(&T, &T) -> bool, -{ - if index >= v.len() { - panic!("partition_at_index index {} greater than length of slice {}", index, v.len()); - } - - if T::IS_ZST { - // Sorting has no meaningful behavior on zero-sized types. Do nothing. - } else if index == v.len() - 1 { - // Find max element and place it in the last position of the array. We're free to use - // `unwrap()` here because we know v must not be empty. - let max_idx = max_index(v, &mut is_less).unwrap(); - v.swap(max_idx, index); - } else if index == 0 { - // Find min element and place it in the first position of the array. We're free to use - // `unwrap()` here because we know v must not be empty. - let min_idx = min_index(v, &mut is_less).unwrap(); - v.swap(min_idx, index); - } else { - partition_at_index_loop(v, index, &mut is_less, None); - } - - let (left, right) = v.split_at_mut(index); - let (pivot, right) = right.split_at_mut(1); - let pivot = &mut pivot[0]; - (left, pivot, right) -} - /// Selection algorithm to select the k-th element from the slice in guaranteed O(n) time. /// This is essentially a quickselect that uses Tukey's Ninther for pivot selection fn median_of_medians bool>(mut v: &mut [T], is_less: &mut F, mut k: usize) { @@ -168,8 +165,8 @@ fn median_of_medians bool>(mut v: &mut [T], is_less: &mut // We now know that `k < v.len() <= isize::MAX` loop { - if v.len() <= MAX_INSERTION { - if v.len() > 1 { + if v.len() <= INSERTION_SORT_THRESHOLD { + if v.len() >= 2 { insertion_sort_shift_left(v, 1, is_less); } return; @@ -232,7 +229,8 @@ fn median_of_ninthers bool>(v: &mut [T], is_less: &mut F) } median_of_medians(&mut v[lo..lo + frac], is_less, pivot); - partition(v, lo + pivot, is_less).0 + + partition(v, lo + pivot, is_less) } /// Moves around the 9 elements at the indices a..i, such that diff --git a/library/core/src/slice/sort/shared/mod.rs b/library/core/src/slice/sort/shared/mod.rs new file mode 100644 index 0000000000000..ad1171bfc6a0a --- /dev/null +++ b/library/core/src/slice/sort/shared/mod.rs @@ -0,0 +1,45 @@ +use crate::marker::Freeze; + +pub(crate) mod pivot; +pub(crate) mod smallsort; + +/// SAFETY: this is safety relevant, how does this interact with the soundness holes in +/// specialization? +#[rustc_unsafe_specialization_marker] +pub(crate) trait FreezeMarker {} + +impl FreezeMarker for T {} + +/// Finds a run of sorted elements starting at the beginning of the slice. +/// +/// Returns the length of the run, and a bool that is false when the run +/// is ascending, and true if the run strictly descending. +#[inline(always)] +pub(crate) fn find_existing_run bool>( + v: &[T], + is_less: &mut F, +) -> (usize, bool) { + let len = v.len(); + if len < 2 { + return (len, false); + } + + // SAFETY: We checked that len >= 2, so 0 and 1 are valid indices. + // This also means that run_len < len implies run_len and run_len - 1 + // are valid indices as well. + unsafe { + let mut run_len = 2; + let strictly_descending = is_less(v.get_unchecked(1), v.get_unchecked(0)); + if strictly_descending { + while run_len < len && is_less(v.get_unchecked(run_len), v.get_unchecked(run_len - 1)) { + run_len += 1; + } + } else { + while run_len < len && !is_less(v.get_unchecked(run_len), v.get_unchecked(run_len - 1)) + { + run_len += 1; + } + } + (run_len, strictly_descending) + } +} diff --git a/library/core/src/slice/sort/shared/pivot.rs b/library/core/src/slice/sort/shared/pivot.rs new file mode 100644 index 0000000000000..255a1eb6c88a8 --- /dev/null +++ b/library/core/src/slice/sort/shared/pivot.rs @@ -0,0 +1,88 @@ +//! This module contains the logic for pivot selection. + +use crate::intrinsics; + +// Recursively select a pseudomedian if above this threshold. +const PSEUDO_MEDIAN_REC_THRESHOLD: usize = 64; + +/// Selects a pivot from `v`. Algorithm taken from glidesort by Orson Peters. +/// +/// This chooses a pivot by sampling an adaptive amount of points, approximating +/// the quality of a median of sqrt(n) elements. +pub fn choose_pivot bool>(v: &[T], is_less: &mut F) -> usize { + // We use unsafe code and raw pointers here because we're dealing with + // heavy recursion. Passing safe slices around would involve a lot of + // branches and function call overhead. + + let len = v.len(); + if len < 8 { + intrinsics::abort(); + } + + // SAFETY: a, b, c point to initialized regions of len_div_8 elements, + // satisfying median3 and median3_rec's preconditions as v_base points + // to an initialized region of n = len elements. + unsafe { + let v_base = v.as_ptr(); + let len_div_8 = len / 8; + + let a = v_base; // [0, floor(n/8)) + let b = v_base.add(len_div_8 * 4); // [4*floor(n/8), 5*floor(n/8)) + let c = v_base.add(len_div_8 * 7); // [7*floor(n/8), 8*floor(n/8)) + + if len < PSEUDO_MEDIAN_REC_THRESHOLD { + median3(&*a, &*b, &*c, is_less).sub_ptr(v_base) + } else { + median3_rec(a, b, c, len_div_8, is_less).sub_ptr(v_base) + } + } +} + +/// Calculates an approximate median of 3 elements from sections a, b, c, or +/// recursively from an approximation of each, if they're large enough. By +/// dividing the size of each section by 8 when recursing we have logarithmic +/// recursion depth and overall sample from f(n) = 3*f(n/8) -> f(n) = +/// O(n^(log(3)/log(8))) ~= O(n^0.528) elements. +/// +/// SAFETY: a, b, c must point to the start of initialized regions of memory of +/// at least n elements. +unsafe fn median3_rec bool>( + mut a: *const T, + mut b: *const T, + mut c: *const T, + n: usize, + is_less: &mut F, +) -> *const T { + // SAFETY: a, b, c still point to initialized regions of n / 8 elements, + // by the exact same logic as in choose_pivot. + unsafe { + if n * 8 >= PSEUDO_MEDIAN_REC_THRESHOLD { + let n8 = n / 8; + a = median3_rec(a, a.add(n8 * 4), a.add(n8 * 7), n8, is_less); + b = median3_rec(b, b.add(n8 * 4), b.add(n8 * 7), n8, is_less); + c = median3_rec(c, c.add(n8 * 4), c.add(n8 * 7), n8, is_less); + } + median3(&*a, &*b, &*c, is_less) + } +} + +/// Calculates the median of 3 elements. +/// +/// SAFETY: a, b, c must be valid initialized elements. +#[inline(always)] +fn median3 bool>(a: &T, b: &T, c: &T, is_less: &mut F) -> *const T { + // Compiler tends to make this branchless when sensible, and avoids the + // third comparison when not. + let x = is_less(a, b); + let y = is_less(a, c); + if x == y { + // If x=y=0 then b, c <= a. In this case we want to return max(b, c). + // If x=y=1 then a < b, c. In this case we want to return min(b, c). + // By toggling the outcome of b < c using XOR x we get this behavior. + let z = is_less(b, c); + if z ^ x { c } else { b } + } else { + // Either c <= a < b or b <= a < c, thus a is our median. + a + } +} diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs new file mode 100644 index 0000000000000..5064c5a0ae55a --- /dev/null +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -0,0 +1,852 @@ +//! This module contains a variety of sort implementations that are optimized for small lengths. + +use crate::mem::{self, ManuallyDrop, MaybeUninit}; +use crate::slice::sort::shared::FreezeMarker; +use crate::{intrinsics, ptr, slice}; + +// It's important to differentiate between SMALL_SORT_THRESHOLD performance for +// small slices and small-sort performance sorting small sub-slices as part of +// the main quicksort loop. For the former, testing showed that the +// representative benchmarks for real-world performance are cold CPU state and +// not single-size hot benchmarks. For the latter the CPU will call them many +// times, so hot benchmarks are fine and more realistic. And it's worth it to +// optimize sorting small sub-slices with more sophisticated solutions than +// insertion sort. + +/// Using a trait allows us to specialize on `Freeze` which in turn allows us to make safe +/// abstractions. +pub(crate) trait StableSmallSortTypeImpl: Sized { + /// For which input length <= return value of this function, is it valid to call `small_sort`. + fn small_sort_threshold() -> usize; + + /// Sorts `v` using strategies optimized for small sizes. + fn small_sort bool>( + v: &mut [Self], + scratch: &mut [MaybeUninit], + is_less: &mut F, + ); +} + +impl StableSmallSortTypeImpl for T { + #[inline(always)] + default fn small_sort_threshold() -> usize { + // Optimal number of comparisons, and good perf. + SMALL_SORT_FALLBACK_THRESHOLD + } + + #[inline(always)] + default fn small_sort bool>( + v: &mut [T], + _scratch: &mut [MaybeUninit], + is_less: &mut F, + ) { + if v.len() >= 2 { + insertion_sort_shift_left(v, 1, is_less); + } + } +} + +impl StableSmallSortTypeImpl for T { + #[inline(always)] + fn small_sort_threshold() -> usize { + SMALL_SORT_GENERAL_THRESHOLD + } + + #[inline(always)] + fn small_sort bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + is_less: &mut F, + ) { + small_sort_general_with_scratch(v, scratch, is_less); + } +} + +/// Using a trait allows us to specialize on `Freeze` which in turn allows us to make safe +/// abstractions. +pub(crate) trait UnstableSmallSortTypeImpl: Sized { + /// For which input length <= return value of this function, is it valid to call `small_sort`. + fn small_sort_threshold() -> usize; + + /// Sorts `v` using strategies optimized for small sizes. + fn small_sort bool>(v: &mut [Self], is_less: &mut F); +} + +impl UnstableSmallSortTypeImpl for T { + #[inline(always)] + default fn small_sort_threshold() -> usize { + SMALL_SORT_FALLBACK_THRESHOLD + } + + #[inline(always)] + default fn small_sort(v: &mut [T], is_less: &mut F) + where + F: FnMut(&T, &T) -> bool, + { + small_sort_fallback(v, is_less); + } +} + +impl UnstableSmallSortTypeImpl for T { + #[inline(always)] + fn small_sort_threshold() -> usize { + ::small_sort_threshold() + } + + #[inline(always)] + fn small_sort(v: &mut [T], is_less: &mut F) + where + F: FnMut(&T, &T) -> bool, + { + ::small_sort(v, is_less); + } +} + +/// FIXME(effects) use original ipnsort approach with choose_unstable_small_sort, +/// as found here . +pub(crate) trait UnstableSmallSortFreezeTypeImpl: Sized + FreezeMarker { + fn small_sort_threshold() -> usize; + + fn small_sort bool>(v: &mut [Self], is_less: &mut F); +} + +impl UnstableSmallSortFreezeTypeImpl for T { + #[inline(always)] + default fn small_sort_threshold() -> usize { + if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + SMALL_SORT_GENERAL_THRESHOLD + } else { + SMALL_SORT_FALLBACK_THRESHOLD + } + } + + #[inline(always)] + default fn small_sort(v: &mut [T], is_less: &mut F) + where + F: FnMut(&T, &T) -> bool, + { + if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + small_sort_general(v, is_less); + } else { + small_sort_fallback(v, is_less); + } + } +} + +/// SAFETY: Only used for run-time optimization heuristic. +#[rustc_unsafe_specialization_marker] +trait CopyMarker {} + +impl CopyMarker for T {} + +impl UnstableSmallSortFreezeTypeImpl for T { + #[inline(always)] + fn small_sort_threshold() -> usize { + if has_efficient_in_place_swap::() + && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE + { + SMALL_SORT_NETWORK_THRESHOLD + } else if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + SMALL_SORT_GENERAL_THRESHOLD + } else { + SMALL_SORT_FALLBACK_THRESHOLD + } + } + + #[inline(always)] + fn small_sort(v: &mut [T], is_less: &mut F) + where + F: FnMut(&T, &T) -> bool, + { + if has_efficient_in_place_swap::() + && (mem::size_of::() * SMALL_SORT_NETWORK_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE + { + small_sort_network(v, is_less); + } else if (mem::size_of::() * SMALL_SORT_GENERAL_SCRATCH_LEN) <= MAX_STACK_ARRAY_SIZE { + small_sort_general(v, is_less); + } else { + small_sort_fallback(v, is_less); + } + } +} + +/// Optimal number of comparisons, and good perf. +const SMALL_SORT_FALLBACK_THRESHOLD: usize = 16; + +/// From a comparison perspective 20 was ~2% more efficient for fully random input, but for +/// wall-clock performance choosing 32 yielded better performance overall. +/// +/// SAFETY: If you change this value, you have to adjust [`small_sort_general`] ! +const SMALL_SORT_GENERAL_THRESHOLD: usize = 32; + +/// [`small_sort_general`] uses [`sort8_stable`] as primitive and does a kind of ping-pong merge, +/// where the output of the first two [`sort8_stable`] calls is stored at the end of the scratch +/// buffer. This simplifies panic handling and avoids additional copies. This affects the required +/// scratch buffer size. +/// +/// SAFETY: If you change this value, you have to adjust [`small_sort_general`] ! +pub(crate) const SMALL_SORT_GENERAL_SCRATCH_LEN: usize = SMALL_SORT_GENERAL_THRESHOLD + 16; + +/// SAFETY: If you change this value, you have to adjust [`small_sort_network`] ! +const SMALL_SORT_NETWORK_THRESHOLD: usize = 32; +const SMALL_SORT_NETWORK_SCRATCH_LEN: usize = SMALL_SORT_NETWORK_THRESHOLD; + +/// Using a stack array, could cause a stack overflow if the type `T` is very large. To be +/// conservative we limit the usage of small-sorts that require a stack array to types that fit +/// within this limit. +const MAX_STACK_ARRAY_SIZE: usize = 4096; + +fn small_sort_fallback bool>(v: &mut [T], is_less: &mut F) { + if v.len() >= 2 { + insertion_sort_shift_left(v, 1, is_less); + } +} + +fn small_sort_general bool>(v: &mut [T], is_less: &mut F) { + let mut stack_array = MaybeUninit::<[T; SMALL_SORT_GENERAL_SCRATCH_LEN]>::uninit(); + + // SAFETY: The memory is backed by `stack_array`, and the operation is safe as long as the len + // is the same. + let scratch = unsafe { + slice::from_raw_parts_mut( + stack_array.as_mut_ptr() as *mut MaybeUninit, + SMALL_SORT_GENERAL_SCRATCH_LEN, + ) + }; + + small_sort_general_with_scratch(v, scratch, is_less); +} + +fn small_sort_general_with_scratch bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + is_less: &mut F, +) { + let len = v.len(); + if len < 2 { + return; + } + + if scratch.len() < len + 16 { + intrinsics::abort(); + } + + let v_base = v.as_mut_ptr(); + let len_div_2 = len / 2; + + // SAFETY: See individual comments. + unsafe { + let scratch_base = scratch.as_mut_ptr() as *mut T; + + let presorted_len = if const { mem::size_of::() <= 16 } && len >= 16 { + // SAFETY: scratch_base is valid and has enough space. + sort8_stable(v_base, scratch_base, scratch_base.add(len), is_less); + sort8_stable( + v_base.add(len_div_2), + scratch_base.add(len_div_2), + scratch_base.add(len + 8), + is_less, + ); + + 8 + } else if len >= 8 { + // SAFETY: scratch_base is valid and has enough space. + sort4_stable(v_base, scratch_base, is_less); + sort4_stable(v_base.add(len_div_2), scratch_base.add(len_div_2), is_less); + + 4 + } else { + ptr::copy_nonoverlapping(v_base, scratch_base, 1); + ptr::copy_nonoverlapping(v_base.add(len_div_2), scratch_base.add(len_div_2), 1); + + 1 + }; + + for offset in [0, len_div_2] { + // SAFETY: at this point dst is initialized with presorted_len elements. + // We extend this to desired_len, src is valid for desired_len elements. + let src = v_base.add(offset); + let dst = scratch_base.add(offset); + let desired_len = if offset == 0 { len_div_2 } else { len - len_div_2 }; + + for i in presorted_len..desired_len { + ptr::copy_nonoverlapping(src.add(i), dst.add(i), 1); + insert_tail(dst, dst.add(i), is_less); + } + } + + // SAFETY: see comment in `CopyOnDrop::drop`. + let drop_guard = CopyOnDrop { src: scratch_base, dst: v_base, len }; + + // SAFETY: at this point scratch_base is fully initialized, allowing us + // to use it as the source of our merge back into the original array. + // If a panic occurs we ensure the original array is restored to a valid + // permutation of the input through drop_guard. This technique is similar + // to ping-pong merging. + bidirectional_merge( + &*ptr::slice_from_raw_parts(drop_guard.src, drop_guard.len), + drop_guard.dst, + is_less, + ); + mem::forget(drop_guard); + } +} + +struct CopyOnDrop { + src: *const T, + dst: *mut T, + len: usize, +} + +impl Drop for CopyOnDrop { + fn drop(&mut self) { + // SAFETY: `src` must contain `len` initialized elements, and dst must + // be valid to write `len` elements. + unsafe { + ptr::copy_nonoverlapping(self.src, self.dst, self.len); + } + } +} + +fn small_sort_network(v: &mut [T], is_less: &mut F) +where + T: FreezeMarker, + F: FnMut(&T, &T) -> bool, +{ + // This implementation is tuned to be efficient for integer types. + + let len = v.len(); + if len < 2 { + return; + } + + if len > SMALL_SORT_NETWORK_SCRATCH_LEN { + intrinsics::abort(); + } + + let mut stack_array = MaybeUninit::<[T; SMALL_SORT_NETWORK_SCRATCH_LEN]>::uninit(); + + let len_div_2 = len / 2; + let no_merge = len < 18; + + let v_base = v.as_mut_ptr(); + let initial_region_len = if no_merge { len } else { len_div_2 }; + // SAFETY: Both possible values of `initial_region_len` are in-bounds. + let mut region = unsafe { &mut *ptr::slice_from_raw_parts_mut(v_base, initial_region_len) }; + + // Avoid compiler unrolling, we *really* don't want that to happen here for binary-size reasons. + loop { + let presorted_len = if region.len() >= 13 { + sort13_optimal(region, is_less); + 13 + } else if region.len() >= 9 { + sort9_optimal(region, is_less); + 9 + } else { + 1 + }; + + insertion_sort_shift_left(region, presorted_len, is_less); + + if no_merge { + return; + } + + if region.as_ptr() != v_base { + break; + } + + // SAFETY: The right side of `v` based on `len_div_2` is guaranteed in-bounds. + unsafe { + region = &mut *ptr::slice_from_raw_parts_mut(v_base.add(len_div_2), len - len_div_2) + }; + } + + // SAFETY: We checked that T is Freeze and thus observation safe. + // Should is_less panic v was not modified in parity_merge and retains it's original input. + // scratch and v must not alias and scratch has v.len() space. + unsafe { + let scratch_base = stack_array.as_mut_ptr() as *mut T; + bidirectional_merge( + &mut *ptr::slice_from_raw_parts_mut(v_base, len), + scratch_base, + is_less, + ); + ptr::copy_nonoverlapping(scratch_base, v_base, len); + } +} + +/// Swap two values in the slice pointed to by `v_base` at the position `a_pos` and `b_pos` if the +/// value at position `b_pos` is less than the one at position `a_pos`. +pub unsafe fn swap_if_less(v_base: *mut T, a_pos: usize, b_pos: usize, is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + // SAFETY: the caller must guarantee that `a` and `b` each added to `v_base` yield valid + // pointers into `v_base`, and are properly aligned, and part of the same allocation. + unsafe { + let v_a = v_base.add(a_pos); + let v_b = v_base.add(b_pos); + + // PANIC SAFETY: if is_less panics, no scratch memory was created and the slice should still be + // in a well defined state, without duplicates. + + // Important to only swap if it is more and not if it is equal. is_less should return false for + // equal, so we don't swap. + let should_swap = is_less(&*v_b, &*v_a); + + // This is a branchless version of swap if. + // The equivalent code with a branch would be: + // + // if should_swap { + // ptr::swap(left, right, 1); + // } + + // The goal is to generate cmov instructions here. + let left_swap = if should_swap { v_b } else { v_a }; + let right_swap = if should_swap { v_a } else { v_b }; + + let right_swap_tmp = ManuallyDrop::new(ptr::read(right_swap)); + ptr::copy(left_swap, v_a, 1); + ptr::copy_nonoverlapping(&*right_swap_tmp, v_b, 1); + } +} + +/// Sorts the first 9 elements of `v` with a fast fixed function. +/// +/// Should `is_less` generate substantial amounts of code the compiler can choose to not inline +/// `swap_if_less`. If the code of a sort impl changes so as to call this function in multiple +/// places, `#[inline(never)]` is recommended to keep binary-size in check. The current design of +/// `small_sort_network` makes sure to only call this once. +fn sort9_optimal(v: &mut [T], is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + if v.len() < 9 { + intrinsics::abort(); + } + + let v_base = v.as_mut_ptr(); + + // Optimal sorting network see: + // https://bertdobbelaere.github.io/sorting_networks.html. + + // SAFETY: We checked the len. + unsafe { + swap_if_less(v_base, 0, 3, is_less); + swap_if_less(v_base, 1, 7, is_less); + swap_if_less(v_base, 2, 5, is_less); + swap_if_less(v_base, 4, 8, is_less); + swap_if_less(v_base, 0, 7, is_less); + swap_if_less(v_base, 2, 4, is_less); + swap_if_less(v_base, 3, 8, is_less); + swap_if_less(v_base, 5, 6, is_less); + swap_if_less(v_base, 0, 2, is_less); + swap_if_less(v_base, 1, 3, is_less); + swap_if_less(v_base, 4, 5, is_less); + swap_if_less(v_base, 7, 8, is_less); + swap_if_less(v_base, 1, 4, is_less); + swap_if_less(v_base, 3, 6, is_less); + swap_if_less(v_base, 5, 7, is_less); + swap_if_less(v_base, 0, 1, is_less); + swap_if_less(v_base, 2, 4, is_less); + swap_if_less(v_base, 3, 5, is_less); + swap_if_less(v_base, 6, 8, is_less); + swap_if_less(v_base, 2, 3, is_less); + swap_if_less(v_base, 4, 5, is_less); + swap_if_less(v_base, 6, 7, is_less); + swap_if_less(v_base, 1, 2, is_less); + swap_if_less(v_base, 3, 4, is_less); + swap_if_less(v_base, 5, 6, is_less); + } +} + +/// Sorts the first 13 elements of `v` with a fast fixed function. +/// +/// Should `is_less` generate substantial amounts of code the compiler can choose to not inline +/// `swap_if_less`. If the code of a sort impl changes so as to call this function in multiple +/// places, `#[inline(never)]` is recommended to keep binary-size in check. The current design of +/// `small_sort_network` makes sure to only call this once. +fn sort13_optimal(v: &mut [T], is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + if v.len() < 13 { + intrinsics::abort(); + } + + let v_base = v.as_mut_ptr(); + + // Optimal sorting network see: + // https://bertdobbelaere.github.io/sorting_networks.html. + + // SAFETY: We checked the len. + unsafe { + swap_if_less(v_base, 0, 12, is_less); + swap_if_less(v_base, 1, 10, is_less); + swap_if_less(v_base, 2, 9, is_less); + swap_if_less(v_base, 3, 7, is_less); + swap_if_less(v_base, 5, 11, is_less); + swap_if_less(v_base, 6, 8, is_less); + swap_if_less(v_base, 1, 6, is_less); + swap_if_less(v_base, 2, 3, is_less); + swap_if_less(v_base, 4, 11, is_less); + swap_if_less(v_base, 7, 9, is_less); + swap_if_less(v_base, 8, 10, is_less); + swap_if_less(v_base, 0, 4, is_less); + swap_if_less(v_base, 1, 2, is_less); + swap_if_less(v_base, 3, 6, is_less); + swap_if_less(v_base, 7, 8, is_less); + swap_if_less(v_base, 9, 10, is_less); + swap_if_less(v_base, 11, 12, is_less); + swap_if_less(v_base, 4, 6, is_less); + swap_if_less(v_base, 5, 9, is_less); + swap_if_less(v_base, 8, 11, is_less); + swap_if_less(v_base, 10, 12, is_less); + swap_if_less(v_base, 0, 5, is_less); + swap_if_less(v_base, 3, 8, is_less); + swap_if_less(v_base, 4, 7, is_less); + swap_if_less(v_base, 6, 11, is_less); + swap_if_less(v_base, 9, 10, is_less); + swap_if_less(v_base, 0, 1, is_less); + swap_if_less(v_base, 2, 5, is_less); + swap_if_less(v_base, 6, 9, is_less); + swap_if_less(v_base, 7, 8, is_less); + swap_if_less(v_base, 10, 11, is_less); + swap_if_less(v_base, 1, 3, is_less); + swap_if_less(v_base, 2, 4, is_less); + swap_if_less(v_base, 5, 6, is_less); + swap_if_less(v_base, 9, 10, is_less); + swap_if_less(v_base, 1, 2, is_less); + swap_if_less(v_base, 3, 4, is_less); + swap_if_less(v_base, 5, 7, is_less); + swap_if_less(v_base, 6, 8, is_less); + swap_if_less(v_base, 2, 3, is_less); + swap_if_less(v_base, 4, 5, is_less); + swap_if_less(v_base, 6, 7, is_less); + swap_if_less(v_base, 8, 9, is_less); + swap_if_less(v_base, 3, 4, is_less); + swap_if_less(v_base, 5, 6, is_less); + } +} + +/// Sorts range [begin, tail] assuming [begin, tail) is already sorted. +/// +/// # Safety +/// begin < tail and p must be valid and initialized for all begin <= p <= tail. +unsafe fn insert_tail bool>(begin: *mut T, tail: *mut T, is_less: &mut F) { + // SAFETY: see individual comments. + unsafe { + // SAFETY: in-bounds as tail > begin. + let mut sift = tail.sub(1); + if !is_less(&*tail, &*sift) { + return; + } + + // SAFETY: after this read tail is never read from again, as we only ever + // read from sift, sift < tail and we only ever decrease sift. Thus this is + // effectively a move, not a copy. Should a panic occur, or we have found + // the correct insertion position, gap_guard ensures the element is moved + // back into the array. + let tmp = ManuallyDrop::new(tail.read()); + let mut gap_guard = CopyOnDrop { src: &*tmp, dst: tail, len: 1 }; + + loop { + // SAFETY: we move sift into the gap (which is valid), and point the + // gap guard destination at sift, ensuring that if a panic occurs the + // gap is once again filled. + ptr::copy_nonoverlapping(sift, gap_guard.dst, 1); + gap_guard.dst = sift; + + if sift == begin { + break; + } + + // SAFETY: we checked that sift != begin, thus this is in-bounds. + sift = sift.sub(1); + if !is_less(&tmp, &*sift) { + break; + } + } + } +} + +/// Sort `v` assuming `v[..offset]` is already sorted. +pub fn insertion_sort_shift_left bool>( + v: &mut [T], + offset: usize, + is_less: &mut F, +) { + let len = v.len(); + if offset == 0 || offset > len { + intrinsics::abort(); + } + + // SAFETY: see individual comments. + unsafe { + // We write this basic loop directly using pointers, as when we use a + // for loop LLVM likes to unroll this loop which we do not want. + // SAFETY: v_end is the one-past-end pointer, and we checked that + // offset <= len, thus tail is also in-bounds. + let v_base = v.as_mut_ptr(); + let v_end = v_base.add(len); + let mut tail = v_base.add(offset); + while tail != v_end { + // SAFETY: v_base and tail are both valid pointers to elements, and + // v_base < tail since we checked offset != 0. + insert_tail(v_base, tail, is_less); + + // SAFETY: we checked that tail is not yet the one-past-end pointer. + tail = tail.add(1); + } + } +} + +/// SAFETY: The caller MUST guarantee that `v_base` is valid for 4 reads and +/// `dst` is valid for 4 writes. The result will be stored in `dst[0..4]`. +pub unsafe fn sort4_stable bool>( + v_base: *const T, + dst: *mut T, + is_less: &mut F, +) { + // By limiting select to picking pointers, we are guaranteed good cmov code-gen + // regardless of type T's size. Further this only does 5 instead of 6 + // comparisons compared to a stable transposition 4 element sorting-network, + // and always copies each element exactly once. + + // SAFETY: all pointers have offset at most 3 from v_base and dst, and are + // thus in-bounds by the precondition. + unsafe { + // Stably create two pairs a <= b and c <= d. + let c1 = is_less(&*v_base.add(1), &*v_base); + let c2 = is_less(&*v_base.add(3), &*v_base.add(2)); + let a = v_base.add(c1 as usize); + let b = v_base.add(!c1 as usize); + let c = v_base.add(2 + c2 as usize); + let d = v_base.add(2 + (!c2 as usize)); + + // Compare (a, c) and (b, d) to identify max/min. We're left with two + // unknown elements, but because we are a stable sort we must know which + // one is leftmost and which one is rightmost. + // c3, c4 | min max unknown_left unknown_right + // 0, 0 | a d b c + // 0, 1 | a b c d + // 1, 0 | c d a b + // 1, 1 | c b a d + let c3 = is_less(&*c, &*a); + let c4 = is_less(&*d, &*b); + let min = select(c3, c, a); + let max = select(c4, b, d); + let unknown_left = select(c3, a, select(c4, c, b)); + let unknown_right = select(c4, d, select(c3, b, c)); + + // Sort the last two unknown elements. + let c5 = is_less(&*unknown_right, &*unknown_left); + let lo = select(c5, unknown_right, unknown_left); + let hi = select(c5, unknown_left, unknown_right); + + ptr::copy_nonoverlapping(min, dst, 1); + ptr::copy_nonoverlapping(lo, dst.add(1), 1); + ptr::copy_nonoverlapping(hi, dst.add(2), 1); + ptr::copy_nonoverlapping(max, dst.add(3), 1); + } + + #[inline(always)] + fn select(cond: bool, if_true: *const T, if_false: *const T) -> *const T { + if cond { if_true } else { if_false } + } +} + +/// SAFETY: The caller MUST guarantee that `v_base` is valid for 8 reads and +/// writes, `scratch_base` and `dst` MUST be valid for 8 writes. The result will +/// be stored in `dst[0..8]`. +unsafe fn sort8_stable bool>( + v_base: *mut T, + dst: *mut T, + scratch_base: *mut T, + is_less: &mut F, +) { + // SAFETY: these pointers are all in-bounds by the precondition of our function. + unsafe { + sort4_stable(v_base, scratch_base, is_less); + sort4_stable(v_base.add(4), scratch_base.add(4), is_less); + } + + // SAFETY: scratch_base[0..8] is now initialized, allowing us to merge back + // into dst. + unsafe { + bidirectional_merge(&*ptr::slice_from_raw_parts(scratch_base, 8), dst, is_less); + } +} + +#[inline(always)] +unsafe fn merge_up bool>( + mut left_src: *const T, + mut right_src: *const T, + mut dst: *mut T, + is_less: &mut F, +) -> (*const T, *const T, *mut T) { + // This is a branchless merge utility function. + // The equivalent code with a branch would be: + // + // if !is_less(&*right_src, &*left_src) { + // ptr::copy_nonoverlapping(left_src, dst, 1); + // left_src = left_src.add(1); + // } else { + // ptr::copy_nonoverlapping(right_src, dst, 1); + // right_src = right_src.add(1); + // } + // dst = dst.add(1); + + // SAFETY: The caller must guarantee that `left_src`, `right_src` are valid + // to read and `dst` is valid to write, while not aliasing. + unsafe { + let is_l = !is_less(&*right_src, &*left_src); + let src = if is_l { left_src } else { right_src }; + ptr::copy_nonoverlapping(src, dst, 1); + right_src = right_src.add(!is_l as usize); + left_src = left_src.add(is_l as usize); + dst = dst.add(1); + } + + (left_src, right_src, dst) +} + +#[inline(always)] +unsafe fn merge_down bool>( + mut left_src: *const T, + mut right_src: *const T, + mut dst: *mut T, + is_less: &mut F, +) -> (*const T, *const T, *mut T) { + // This is a branchless merge utility function. + // The equivalent code with a branch would be: + // + // if !is_less(&*right_src, &*left_src) { + // ptr::copy_nonoverlapping(right_src, dst, 1); + // right_src = right_src.wrapping_sub(1); + // } else { + // ptr::copy_nonoverlapping(left_src, dst, 1); + // left_src = left_src.wrapping_sub(1); + // } + // dst = dst.sub(1); + + // SAFETY: The caller must guarantee that `left_src`, `right_src` are valid + // to read and `dst` is valid to write, while not aliasing. + unsafe { + let is_l = !is_less(&*right_src, &*left_src); + let src = if is_l { right_src } else { left_src }; + ptr::copy_nonoverlapping(src, dst, 1); + right_src = right_src.wrapping_sub(is_l as usize); + left_src = left_src.wrapping_sub(!is_l as usize); + dst = dst.sub(1); + } + + (left_src, right_src, dst) +} + +/// Merge v assuming v[..len / 2] and v[len / 2..] are sorted. +/// +/// Original idea for bi-directional merging by Igor van den Hoven (quadsort), +/// adapted to only use merge up and down. In contrast to the original +/// parity_merge function, it performs 2 writes instead of 4 per iteration. +/// +/// # Safety +/// The caller must guarantee that `dst` is valid for v.len() writes. +/// Also `v.as_ptr()` and `dst` must not alias and v.len() must be >= 2. +/// +/// Note that T must be Freeze, the comparison function is evaluated on outdated +/// temporary 'copies' that may not end up in the final array. +unsafe fn bidirectional_merge bool>( + v: &[T], + dst: *mut T, + is_less: &mut F, +) { + // It helps to visualize the merge: + // + // Initial: + // + // |dst (in dst) + // |left |right + // v v + // [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] + // ^ ^ + // |left_rev |right_rev + // |dst_rev (in dst) + // + // After: + // + // |dst (in dst) + // |left | |right + // v v v + // [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] + // ^ ^ ^ + // |left_rev | |right_rev + // |dst_rev (in dst) + // + // In each iteration one of left or right moves up one position, and one of + // left_rev or right_rev moves down one position, whereas dst always moves + // up one position and dst_rev always moves down one position. Assuming + // the input was sorted and the comparison function is correctly implemented + // at the end we will have left == left_rev + 1, and right == right_rev + 1, + // fully consuming the input having written it to dst. + + let len = v.len(); + let src = v.as_ptr(); + + let len_div_2 = len / 2; + + // SAFETY: The caller has to ensure that len >= 2. + unsafe { + intrinsics::assume(len_div_2 != 0); // This can avoid useless code-gen. + } + + // SAFETY: no matter what the result of the user-provided comparison function + // is, all 4 read pointers will always be in-bounds. Writing `dst` and `dst_rev` + // will always be in bounds if the caller guarantees that `dst` is valid for + // `v.len()` writes. + unsafe { + let mut left = src; + let mut right = src.add(len_div_2); + let mut dst = dst; + + let mut left_rev = src.add(len_div_2 - 1); + let mut right_rev = src.add(len - 1); + let mut dst_rev = dst.add(len - 1); + + for _ in 0..len_div_2 { + (left, right, dst) = merge_up(left, right, dst, is_less); + (left_rev, right_rev, dst_rev) = merge_down(left_rev, right_rev, dst_rev, is_less); + } + + let left_end = left_rev.wrapping_add(1); + let right_end = right_rev.wrapping_add(1); + + // Odd length, so one element is left unconsumed in the input. + if len % 2 != 0 { + let left_nonempty = left < left_end; + let last_src = if left_nonempty { left } else { right }; + ptr::copy_nonoverlapping(last_src, dst, 1); + left = left.add(left_nonempty as usize); + right = right.add((!left_nonempty) as usize); + } + + // We now should have consumed the full input exactly once. This can + // only fail if the comparison operator fails to be Ord, in which case + // we will panic and never access the inconsistent state in dst. + if left != left_end || right != right_end { + panic_on_ord_violation(); + } + } +} + +#[inline(never)] +fn panic_on_ord_violation() -> ! { + panic!("Ord violation"); +} + +#[must_use] +pub(crate) const fn has_efficient_in_place_swap() -> bool { + // Heuristic that holds true on all tested 64-bit capable architectures. + mem::size_of::() <= 8 // mem::size_of::() +} diff --git a/library/core/src/slice/sort/stable/drift.rs b/library/core/src/slice/sort/stable/drift.rs new file mode 100644 index 0000000000000..644e75a4581e9 --- /dev/null +++ b/library/core/src/slice/sort/stable/drift.rs @@ -0,0 +1,298 @@ +//! This module contains the hybrid top-level loop combining bottom-up Mergesort with top-down +//! Quicksort. + +use crate::mem::MaybeUninit; +use crate::slice::sort::shared::find_existing_run; +use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl; +use crate::slice::sort::stable::merge::merge; +use crate::slice::sort::stable::quicksort::quicksort; +use crate::{cmp, intrinsics}; + +/// Sorts `v` based on comparison function `is_less`. If `eager_sort` is true, +/// it will only do small-sorts and physical merges, ensuring O(N * log(N)) +/// worst-case complexity. `scratch.len()` must be at least `max(v.len() / 2, +/// MIN_SMALL_SORT_SCRATCH_LEN)` otherwise the implementation may abort. +/// Fully ascending and descending inputs will be sorted with exactly N - 1 +/// comparisons. +/// +/// This is the main loop for driftsort, which uses powersort's heuristic to +/// determine in which order to merge runs, see below for details. +pub fn sort bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + eager_sort: bool, + is_less: &mut F, +) { + let len = v.len(); + if len < 2 { + return; // Removing this length check *increases* code size. + } + let scale_factor = merge_tree_scale_factor(len); + + // It's important to have a relatively high entry barrier for pre-sorted + // runs, as the presence of a single such run will force on average several + // merge operations and shrink the maximum quicksort size a lot. For that + // reason we use sqrt(len) as our pre-sorted run threshold. + const MIN_SQRT_RUN_LEN: usize = 64; + let min_good_run_len = if len <= (MIN_SQRT_RUN_LEN * MIN_SQRT_RUN_LEN) { + // For small input length `MIN_SQRT_RUN_LEN` would break pattern + // detection of full or nearly sorted inputs. + cmp::min(len - len / 2, MIN_SQRT_RUN_LEN) + } else { + sqrt_approx(len) + }; + + // (stack_len, runs, desired_depths) together form a stack maintaining run + // information for the powersort heuristic. desired_depths[i] is the desired + // depth of the merge node that merges runs[i] with the run that comes after + // it. + let mut stack_len = 0; + let mut run_storage = MaybeUninit::<[DriftsortRun; 66]>::uninit(); + let runs: *mut DriftsortRun = run_storage.as_mut_ptr().cast(); + let mut desired_depth_storage = MaybeUninit::<[u8; 66]>::uninit(); + let desired_depths: *mut u8 = desired_depth_storage.as_mut_ptr().cast(); + + let mut scan_idx = 0; + let mut prev_run = DriftsortRun::new_sorted(0); // Initial dummy run. + loop { + // Compute the next run and the desired depth of the merge node between + // prev_run and next_run. On the last iteration we create a dummy run + // with root-level desired depth to fully collapse the merge tree. + let (next_run, desired_depth); + if scan_idx < len { + next_run = + create_run(&mut v[scan_idx..], scratch, min_good_run_len, eager_sort, is_less); + desired_depth = merge_tree_depth( + scan_idx - prev_run.len(), + scan_idx, + scan_idx + next_run.len(), + scale_factor, + ); + } else { + next_run = DriftsortRun::new_sorted(0); + desired_depth = 0; + }; + + // Process the merge nodes between earlier runs[i] that have a desire to + // be deeper in the merge tree than the merge node for the splitpoint + // between prev_run and next_run. + // + // SAFETY: first note that this is the only place we modify stack_len, + // runs or desired depths. We maintain the following invariants: + // 1. The first stack_len elements of runs/desired_depths are initialized. + // 2. For all valid i > 0, desired_depths[i] < desired_depths[i+1]. + // 3. The sum of all valid runs[i].len() plus prev_run.len() equals + // scan_idx. + unsafe { + while stack_len > 1 && *desired_depths.add(stack_len - 1) >= desired_depth { + // Desired depth greater than the upcoming desired depth, pop + // left neighbor run from stack and merge into prev_run. + let left = *runs.add(stack_len - 1); + let merged_len = left.len() + prev_run.len(); + let merge_start_idx = scan_idx - merged_len; + let merge_slice = v.get_unchecked_mut(merge_start_idx..scan_idx); + prev_run = logical_merge(merge_slice, scratch, left, prev_run, is_less); + stack_len -= 1; + } + + // We now know that desired_depths[stack_len - 1] < desired_depth, + // maintaining our invariant. This also guarantees we don't overflow + // the stack as merge_tree_depth(..) <= 64 and thus we can only have + // 64 distinct values on the stack before pushing, plus our initial + // dummy run, while our capacity is 66. + *runs.add(stack_len) = prev_run; + *desired_depths.add(stack_len) = desired_depth; + stack_len += 1; + } + + // Break before overriding the last run with our dummy run. + if scan_idx >= len { + break; + } + + scan_idx += next_run.len(); + prev_run = next_run; + } + + if !prev_run.sorted() { + stable_quicksort(v, scratch, is_less); + } +} + +// Nearly-Optimal Mergesorts: Fast, Practical Sorting Methods That Optimally +// Adapt to Existing Runs by J. Ian Munro and Sebastian Wild. +// +// This method forms a binary merge tree, where each internal node corresponds +// to a splitting point between the adjacent runs that have to be merged. If we +// visualize our array as the number line from 0 to 1, we want to find the +// dyadic fraction with smallest denominator that lies between the midpoints of +// our to-be-merged slices. The exponent in the dyadic fraction indicates the +// desired depth in the binary merge tree this internal node wishes to have. +// This does not always correspond to the actual depth due to the inherent +// imbalance in runs, but we follow it as closely as possible. +// +// As an optimization we rescale the number line from [0, 1) to [0, 2^62). Then +// finding the simplest dyadic fraction between midpoints corresponds to finding +// the most significant bit difference of the midpoints. We save scale_factor = +// ceil(2^62 / n) to perform this rescaling using a multiplication, avoiding +// having to repeatedly do integer divides. This rescaling isn't exact when n is +// not a power of two since we use integers and not reals, but the result is +// very close, and in fact when n < 2^30 the resulting tree is equivalent as the +// approximation errors stay entirely in the lower order bits. +// +// Thus for the splitting point between two adjacent slices [a, b) and [b, c) +// the desired depth of the corresponding merge node is CLZ((a+b)*f ^ (b+c)*f), +// where CLZ counts the number of leading zeros in an integer and f is our scale +// factor. Note that we omitted the division by two in the midpoint +// calculations, as this simply shifts the bits by one position (and thus always +// adds one to the result), and we only care about the relative depths. +// +// Finally, if we try to upper bound x = (a+b)*f giving x = (n-1 + n) * ceil(2^62 / n) then +// x < (2^62 / n + 1) * 2n +// x < 2^63 + 2n +// So as long as n < 2^62 we find that x < 2^64, meaning our operations do not +// overflow. +#[inline(always)] +fn merge_tree_scale_factor(n: usize) -> u64 { + if usize::BITS > u64::BITS { + panic!("Platform not supported"); + } + + ((1 << 62) + n as u64 - 1) / n as u64 +} + +// Note: merge_tree_depth output is < 64 when left < right as f*x and f*y must +// differ in some bit, and is <= 64 always. +#[inline(always)] +fn merge_tree_depth(left: usize, mid: usize, right: usize, scale_factor: u64) -> u8 { + let x = left as u64 + mid as u64; + let y = mid as u64 + right as u64; + ((scale_factor * x) ^ (scale_factor * y)).leading_zeros() as u8 +} + +fn sqrt_approx(n: usize) -> usize { + // Note that sqrt(n) = n^(1/2), and that 2^log2(n) = n. We combine these + // two facts to approximate sqrt(n) as 2^(log2(n) / 2). Because our integer + // log floors we want to add 0.5 to compensate for this on average, so our + // initial approximation is 2^((1 + floor(log2(n))) / 2). + // + // We then apply an iteration of Newton's method to improve our + // approximation, which for sqrt(n) is a1 = (a0 + n / a0) / 2. + // + // Finally we note that the exponentiation / division can be done directly + // with shifts. We OR with 1 to avoid zero-checks in the integer log. + let ilog = (n | 1).ilog2(); + let shift = (1 + ilog) / 2; + ((1 << shift) + (n >> shift)) / 2 +} + +// Lazy logical runs as in Glidesort. +#[inline(always)] +fn logical_merge bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + left: DriftsortRun, + right: DriftsortRun, + is_less: &mut F, +) -> DriftsortRun { + // If one or both of the runs are sorted do a physical merge, using + // quicksort to sort the unsorted run if present. We also *need* to + // physically merge if the combined runs would not fit in the scratch space + // anymore (as this would mean we are no longer able to quicksort them). + let len = v.len(); + let can_fit_in_scratch = len <= scratch.len(); + if !can_fit_in_scratch || left.sorted() || right.sorted() { + if !left.sorted() { + stable_quicksort(&mut v[..left.len()], scratch, is_less); + } + if !right.sorted() { + stable_quicksort(&mut v[left.len()..], scratch, is_less); + } + merge(v, scratch, left.len(), is_less); + + DriftsortRun::new_sorted(len) + } else { + DriftsortRun::new_unsorted(len) + } +} + +/// Creates a new logical run. +/// +/// A logical run can either be sorted or unsorted. If there is a pre-existing +/// run that clears the `min_good_run_len` threshold it is returned as a sorted +/// run. If not, the result depends on the value of `eager_sort`. If it is true, +/// then a sorted run of length `T::SMALL_SORT_THRESHOLD` is returned, and if it +/// is false an unsorted run of length `min_good_run_len` is returned. +fn create_run bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + min_good_run_len: usize, + eager_sort: bool, + is_less: &mut F, +) -> DriftsortRun { + let len = v.len(); + if len >= min_good_run_len { + let (run_len, was_reversed) = find_existing_run(v, is_less); + + // SAFETY: find_existing_run promises to return a valid run_len. + unsafe { intrinsics::assume(run_len <= len) }; + + if run_len >= min_good_run_len { + if was_reversed { + v[..run_len].reverse(); + } + + return DriftsortRun::new_sorted(run_len); + } + } + + if eager_sort { + // We call quicksort with a len that will immediately call small-sort. + // By not calling the small-sort directly here it can always be inlined into + // the quicksort itself, making the recursive base case faster and is generally + // more binary-size efficient. + let eager_run_len = cmp::min(T::small_sort_threshold(), len); + quicksort(&mut v[..eager_run_len], scratch, 0, None, is_less); + DriftsortRun::new_sorted(eager_run_len) + } else { + DriftsortRun::new_unsorted(cmp::min(min_good_run_len, len)) + } +} + +fn stable_quicksort bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + is_less: &mut F, +) { + // Limit the number of imbalanced partitions to `2 * floor(log2(len))`. + // The binary OR by one is used to eliminate the zero-check in the logarithm. + let limit = 2 * (v.len() | 1).ilog2(); + quicksort(v, scratch, limit, None, is_less); +} + +/// Compactly stores the length of a run, and whether or not it is sorted. This +/// can always fit in a `usize` because the maximum slice length is [`isize::MAX`]. +#[derive(Copy, Clone)] +struct DriftsortRun(usize); + +impl DriftsortRun { + #[inline(always)] + fn new_sorted(length: usize) -> Self { + Self((length << 1) | 1) + } + + #[inline(always)] + fn new_unsorted(length: usize) -> Self { + Self(length << 1) + } + + #[inline(always)] + fn sorted(self) -> bool { + self.0 & 1 == 1 + } + + #[inline(always)] + fn len(self) -> usize { + self.0 >> 1 + } +} diff --git a/library/core/src/slice/sort/stable/merge.rs b/library/core/src/slice/sort/stable/merge.rs new file mode 100644 index 0000000000000..0cb21740795b7 --- /dev/null +++ b/library/core/src/slice/sort/stable/merge.rs @@ -0,0 +1,150 @@ +//! This module contains logic for performing a merge of two sorted sub-slices. + +use crate::mem::MaybeUninit; +use crate::{cmp, ptr}; + +/// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `scratch` as +/// temporary storage, and stores the result into `v[..]`. +pub fn merge bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + mid: usize, + is_less: &mut F, +) { + let len = v.len(); + + if mid == 0 || mid >= len || scratch.len() < cmp::min(mid, len - mid) { + return; + } + + // SAFETY: We checked that the two slices are non-empty and `mid` is in-bounds. + // We checked that the buffer `scratch` has enough capacity to hold a copy of + // the shorter slice. `merge_up` and `merge_down` are written in such a way that + // they uphold the contract described in `MergeState::drop`. + unsafe { + // The merge process first copies the shorter run into `buf`. Then it traces + // the newly copied run and the longer run forwards (or backwards), comparing + // their next unconsumed elements and copying the lesser (or greater) one into `v`. + // + // As soon as the shorter run is fully consumed, the process is done. If the + // longer run gets consumed first, then we must copy whatever is left of the + // shorter run into the remaining gap in `v`. + // + // Intermediate state of the process is always tracked by `gap`, which serves + // two purposes: + // 1. Protects integrity of `v` from panics in `is_less`. + // 2. Fills the remaining gap in `v` if the longer run gets consumed first. + + let buf = MaybeUninit::slice_as_mut_ptr(scratch); + + let v_base = v.as_mut_ptr(); + let v_mid = v_base.add(mid); + let v_end = v_base.add(len); + + let left_len = mid; + let right_len = len - mid; + + let left_is_shorter = left_len <= right_len; + let save_base = if left_is_shorter { v_base } else { v_mid }; + let save_len = if left_is_shorter { left_len } else { right_len }; + + ptr::copy_nonoverlapping(save_base, buf, save_len); + + let mut merge_state = MergeState { start: buf, end: buf.add(save_len), dst: save_base }; + + if left_is_shorter { + merge_state.merge_up(v_mid, v_end, is_less); + } else { + merge_state.merge_down(v_base, buf, v_end, is_less); + } + // Finally, `merge_state` gets dropped. If the shorter run was not fully + // consumed, whatever remains of it will now be copied into the hole in `v`. + } +} + +// When dropped, copies the range `start..end` into `dst..`. +struct MergeState { + start: *mut T, + end: *mut T, + dst: *mut T, +} + +impl MergeState { + /// # Safety + /// The caller MUST guarantee that `self` is initialized in a way where `start -> end` is + /// the longer sub-slice and so that `dst` can be written to at least the shorter sub-slice + /// length times. In addition `start -> end` and `right -> right_end` MUST be valid to be + /// read. This function MUST only be called once. + unsafe fn merge_up bool>( + &mut self, + mut right: *const T, + right_end: *const T, + is_less: &mut F, + ) { + // SAFETY: See function safety comment. + unsafe { + let left = &mut self.start; + let out = &mut self.dst; + + while *left != self.end && right as *const T != right_end { + let consume_left = !is_less(&*right, &**left); + + let src = if consume_left { *left } else { right }; + ptr::copy_nonoverlapping(src, *out, 1); + + *left = left.add(consume_left as usize); + right = right.add(!consume_left as usize); + + *out = out.add(1); + } + } + } + + /// # Safety + /// The caller MUST guarantee that `self` is initialized in a way where `left_end <- dst` is + /// the shorter sub-slice and so that `out` can be written to at least the shorter sub-slice + /// length times. In addition `left_end <- dst` and `right_end <- end` MUST be valid to be + /// read. This function MUST only be called once. + unsafe fn merge_down bool>( + &mut self, + left_end: *const T, + right_end: *const T, + mut out: *mut T, + is_less: &mut F, + ) { + // SAFETY: See function safety comment. + unsafe { + loop { + let left = self.dst.sub(1); + let right = self.end.sub(1); + out = out.sub(1); + + let consume_left = is_less(&*right, &*left); + + let src = if consume_left { left } else { right }; + ptr::copy_nonoverlapping(src, out, 1); + + self.dst = left.add(!consume_left as usize); + self.end = right.add(consume_left as usize); + + if self.dst as *const T == left_end || self.end as *const T == right_end { + break; + } + } + } + } +} + +impl Drop for MergeState { + fn drop(&mut self) { + // SAFETY: The user of MergeState MUST ensure, that at any point this drop + // impl MAY run, for example when the user provided `is_less` panics, that + // copying the contiguous region between `start` and `end` to `dst` will + // leave the input slice `v` with each original element and all possible + // modifications observed. + unsafe { + let len = self.end.sub_ptr(self.start); + ptr::copy_nonoverlapping(self.start, self.dst, len); + } + } +} diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs new file mode 100644 index 0000000000000..a383b0f589ccf --- /dev/null +++ b/library/core/src/slice/sort/stable/mod.rs @@ -0,0 +1,114 @@ +//! This module contains the entry points for `slice::sort`. + +use crate::mem::{self, MaybeUninit, SizedTypeProperties}; +use crate::slice::sort::shared::smallsort::{ + insertion_sort_shift_left, StableSmallSortTypeImpl, SMALL_SORT_GENERAL_SCRATCH_LEN, +}; +use crate::{cmp, intrinsics}; + +pub(crate) mod drift; +pub(crate) mod merge; +pub(crate) mod quicksort; + +/// Stable sort called driftsort by Orson Peters and Lukas Bergdoll. +/// Design document: +/// +/// +/// Upholds all safety properties outlined here: +/// +#[inline(always)] +pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less: &mut F) { + // Arrays of zero-sized types are always all-equal, and thus sorted. + if T::IS_ZST { + return; + } + + // Instrumenting the standard library showed that 90+% of the calls to sort + // by rustc are either of size 0 or 1. + let len = v.len(); + if intrinsics::likely(len < 2) { + return; + } + + // More advanced sorting methods than insertion sort are faster if called in + // a hot loop for small inputs, but for general-purpose code the small + // binary size of insertion sort is more important. The instruction cache in + // modern processors is very valuable, and for a single sort call in general + // purpose code any gains from an advanced method are cancelled by i-cache + // misses during the sort, and thrashing the i-cache for surrounding code. + const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; + if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { + insertion_sort_shift_left(v, 1, is_less); + return; + } + + driftsort_main::(v, is_less); +} + +/// See [`sort`] +/// +/// Deliberately don't inline the main sorting routine entrypoint to ensure the +/// inlined insertion sort i-cache footprint remains minimal. +#[inline(never)] +fn driftsort_main bool, BufT: BufGuard>(v: &mut [T], is_less: &mut F) { + // By allocating n elements of memory we can ensure the entire input can + // be sorted using stable quicksort, which allows better performance on + // random and low-cardinality distributions. However, we still want to + // reduce our memory usage to n / 2 for large inputs. We do this by scaling + // our allocation as max(n / 2, min(n, 8MB)), ensuring we scale like n for + // small inputs and n / 2 for large inputs, without a sudden drop off. We + // also need to ensure our alloc >= MIN_SMALL_SORT_SCRATCH_LEN, as the + // small-sort always needs this much memory. + const MAX_FULL_ALLOC_BYTES: usize = 8_000_000; // 8MB + let max_full_alloc = MAX_FULL_ALLOC_BYTES / mem::size_of::(); + let len = v.len(); + let alloc_len = + cmp::max(cmp::max(len / 2, cmp::min(len, max_full_alloc)), SMALL_SORT_GENERAL_SCRATCH_LEN); + + // For small inputs 4KiB of stack storage suffices, which allows us to avoid + // calling the (de-)allocator. Benchmarks showed this was quite beneficial. + let mut stack_buf = AlignedStorage::::new(); + let stack_scratch = stack_buf.as_uninit_slice_mut(); + let mut heap_buf; + let scratch = if stack_scratch.len() >= alloc_len { + stack_scratch + } else { + heap_buf = BufT::with_capacity(alloc_len); + heap_buf.as_uninit_slice_mut() + }; + + // For small inputs using quicksort is not yet beneficial, and a single + // small-sort or two small-sorts plus a single merge outperforms it, so use + // eager mode. + let eager_sort = len <= T::small_sort_threshold() * 2; + crate::slice::sort::stable::drift::sort(v, scratch, eager_sort, is_less); +} + +#[doc(hidden)] +/// Abstracts owned memory buffer, so that sort code can live in core where no allocation is +/// possible. This trait can then be implemented in a place that has access to allocation. +pub trait BufGuard { + /// Creates new buffer that holds at least `capacity` memory. + fn with_capacity(capacity: usize) -> Self; + /// Returns mutable access to uninitialized memory owned by the buffer. + fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit]; +} + +#[repr(C)] +struct AlignedStorage { + _align: [T; 0], + storage: [MaybeUninit; N], +} + +impl AlignedStorage { + fn new() -> Self { + Self { _align: [], storage: [const { MaybeUninit::uninit() }; N] } + } + + fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit] { + let len = N / mem::size_of::(); + + // SAFETY: `_align` ensures we are correctly aligned. + unsafe { core::slice::from_raw_parts_mut(self.storage.as_mut_ptr().cast(), len) } + } +} diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs new file mode 100644 index 0000000000000..3319d67ab52fa --- /dev/null +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -0,0 +1,257 @@ +//! This module contains a stable quicksort and partition implementation. + +use crate::mem::{self, ManuallyDrop, MaybeUninit}; +use crate::slice::sort::shared::pivot::choose_pivot; +use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl; +use crate::slice::sort::shared::FreezeMarker; +use crate::{intrinsics, ptr}; + +/// Sorts `v` recursively using quicksort. +/// +/// `limit` when initialized with `c*log(v.len())` for some c ensures we do not +/// overflow the stack or go quadratic. +#[inline(never)] +pub fn quicksort bool>( + mut v: &mut [T], + scratch: &mut [MaybeUninit], + mut limit: u32, + mut left_ancestor_pivot: Option<&T>, + is_less: &mut F, +) { + loop { + let len = v.len(); + + if len <= T::small_sort_threshold() { + T::small_sort(v, scratch, is_less); + return; + } + + if limit == 0 { + // We have had too many bad pivots, switch to O(n log n) fallback + // algorithm. In our case that is driftsort in eager mode. + crate::slice::sort::stable::drift::sort(v, scratch, true, is_less); + return; + } + limit -= 1; + + let pivot_pos = choose_pivot(v, is_less); + // SAFETY: choose_pivot promises to return a valid pivot index. + unsafe { + intrinsics::assume(pivot_pos < v.len()); + } + + // SAFETY: We only access the temporary copy for Freeze types, otherwise + // self-modifications via `is_less` would not be observed and this would + // be unsound. Our temporary copy does not escape this scope. + let pivot_copy = unsafe { ManuallyDrop::new(ptr::read(&v[pivot_pos])) }; + let pivot_ref = (!has_direct_interior_mutability::()).then_some(&*pivot_copy); + + // We choose a pivot, and check if this pivot is equal to our left + // ancestor. If true, we do a partition putting equal elements on the + // left and do not recurse on it. This gives O(n log k) sorting for k + // distinct values, a strategy borrowed from pdqsort. For types with + // interior mutability we can't soundly create a temporary copy of the + // ancestor pivot, and use left_partition_len == 0 as our method for + // detecting when we re-use a pivot, which means we do at most three + // partition operations with pivot p instead of the optimal two. + let mut perform_equal_partition = false; + if let Some(la_pivot) = left_ancestor_pivot { + perform_equal_partition = !is_less(la_pivot, &v[pivot_pos]); + } + + let mut left_partition_len = 0; + if !perform_equal_partition { + left_partition_len = stable_partition(v, scratch, pivot_pos, false, is_less); + perform_equal_partition = left_partition_len == 0; + } + + if perform_equal_partition { + let mid_eq = stable_partition(v, scratch, pivot_pos, true, &mut |a, b| !is_less(b, a)); + v = &mut v[mid_eq..]; + left_ancestor_pivot = None; + continue; + } + + // Process left side with the next loop iter, right side with recursion. + let (left, right) = v.split_at_mut(left_partition_len); + quicksort(right, scratch, limit, pivot_ref, is_less); + v = left; + } +} + +/// Partitions `v` using pivot `p = v[pivot_pos]` and returns the number of +/// elements less than `p`. The relative order of elements that compare < p and +/// those that compare >= p is preserved - it is a stable partition. +/// +/// If `is_less` is not a strict total order or panics, `scratch.len() < v.len()`, +/// or `pivot_pos >= v.len()`, the result and `v`'s state is sound but unspecified. +fn stable_partition bool>( + v: &mut [T], + scratch: &mut [MaybeUninit], + pivot_pos: usize, + pivot_goes_left: bool, + is_less: &mut F, +) -> usize { + let len = v.len(); + + if intrinsics::unlikely(scratch.len() < len || pivot_pos >= len) { + core::intrinsics::abort() + } + + let v_base = v.as_ptr(); + let scratch_base = MaybeUninit::slice_as_mut_ptr(scratch); + + // The core idea is to write the values that compare as less-than to the left + // side of `scratch`, while the values that compared as greater or equal than + // `v[pivot_pos]` go to the right side of `scratch` in reverse. See + // PartitionState for details. + + // SAFETY: see individual comments. + unsafe { + // SAFETY: we made sure the scratch has length >= len and that pivot_pos + // is in-bounds. v and scratch are disjoint slices. + let pivot = v_base.add(pivot_pos); + let mut state = PartitionState::new(v_base, scratch_base, len); + + let mut pivot_in_scratch = ptr::null_mut(); + let mut loop_end_pos = pivot_pos; + + // SAFETY: this loop is equivalent to calling state.partition_one + // exactly len times. + loop { + // Ideally the outer loop won't be unrolled, to save binary size, + // but we do want the inner loop to be unrolled for small types, as + // this gave significant performance boosts in benchmarks. Unrolling + // through for _ in 0..UNROLL_LEN { .. } instead of manually improves + // compile times but has a ~10-20% performance penalty on opt-level=s. + if const { mem::size_of::() <= 16 } { + const UNROLL_LEN: usize = 4; + let unroll_end = v_base.add(loop_end_pos.saturating_sub(UNROLL_LEN - 1)); + while state.scan < unroll_end { + state.partition_one(is_less(&*state.scan, &*pivot)); + state.partition_one(is_less(&*state.scan, &*pivot)); + state.partition_one(is_less(&*state.scan, &*pivot)); + state.partition_one(is_less(&*state.scan, &*pivot)); + } + } + + let loop_end = v_base.add(loop_end_pos); + while state.scan < loop_end { + state.partition_one(is_less(&*state.scan, &*pivot)); + } + + if loop_end_pos == len { + break; + } + + // We avoid comparing pivot with itself, as this could create deadlocks for + // certain comparison operators. We also store its location later for later. + pivot_in_scratch = state.partition_one(pivot_goes_left); + + loop_end_pos = len; + } + + // `pivot` must be copied into its correct position again, because a + // comparison operator might have modified it. + if has_direct_interior_mutability::() { + ptr::copy_nonoverlapping(pivot, pivot_in_scratch, 1); + } + + // SAFETY: partition_one being called exactly len times guarantees that scratch + // is initialized with a permuted copy of `v`, and that num_left <= v.len(). + // Copying scratch[0..num_left] and scratch[num_left..v.len()] back is thus + // sound, as the values in scratch will never be read again, meaning our copies + // semantically act as moves, permuting `v`. + + // Copy all the elements < p directly from swap to v. + let v_base = v.as_mut_ptr(); + ptr::copy_nonoverlapping(scratch_base, v_base, state.num_left); + + // Copy the elements >= p in reverse order. + for i in 0..len - state.num_left { + ptr::copy_nonoverlapping( + scratch_base.add(len - 1 - i), + v_base.add(state.num_left + i), + 1, + ); + } + + state.num_left + } +} + +struct PartitionState { + // The start of the scratch auxiliary memory. + scratch_base: *mut T, + // The current element that is being looked at, scans left to right through slice. + scan: *const T, + // Counts the number of elements that went to the left side, also works around: + // https://github.com/rust-lang/rust/issues/117128 + num_left: usize, + // Reverse scratch output pointer. + scratch_rev: *mut T, +} + +impl PartitionState { + /// # Safety + /// + /// `scan` and `scratch` must point to valid disjoint buffers of length `len`. The + /// scan buffer must be initialized. + unsafe fn new(scan: *const T, scratch: *mut T, len: usize) -> Self { + // SAFETY: See function safety comment. + unsafe { Self { scratch_base: scratch, scan, num_left: 0, scratch_rev: scratch.add(len) } } + } + + /// Depending on the value of `towards_left` this function will write a value + /// to the growing left or right side of the scratch memory. This forms the + /// branchless core of the partition. + /// + /// # Safety + /// + /// This function may be called at most `len` times. If it is called exactly + /// `len` times the scratch buffer then contains a copy of each element from + /// the scan buffer exactly once - a permutation, and num_left <= len. + unsafe fn partition_one(&mut self, towards_left: bool) -> *mut T { + // SAFETY: see individual comments. + unsafe { + // SAFETY: in-bounds because this function is called at most len times, and thus + // right now is incremented at most len - 1 times. Similarly, num_left < len and + // num_right < len, where num_right == i - num_left at the start of the ith + // iteration (zero-indexed). + self.scratch_rev = self.scratch_rev.sub(1); + + // SAFETY: now we have scratch_rev == base + len - (i + 1). This means + // scratch_rev + num_left == base + len - 1 - num_right < base + len. + let dst_base = if towards_left { self.scratch_base } else { self.scratch_rev }; + let dst = dst_base.add(self.num_left); + ptr::copy_nonoverlapping(self.scan, dst, 1); + + self.num_left += towards_left as usize; + self.scan = self.scan.add(1); + dst + } + } +} + +trait IsFreeze { + fn is_freeze() -> bool; +} + +impl IsFreeze for T { + default fn is_freeze() -> bool { + false + } +} +impl IsFreeze for T { + fn is_freeze() -> bool { + true + } +} + +#[must_use] +fn has_direct_interior_mutability() -> bool { + // If a type has interior mutability it may alter itself during comparison + // in a way that must be preserved after the sort operation concludes. + // Otherwise a type like Mutex>> could lead to double free. + !T::is_freeze() +} diff --git a/library/core/src/slice/sort/unstable/heapsort.rs b/library/core/src/slice/sort/unstable/heapsort.rs new file mode 100644 index 0000000000000..27e2ad588ea09 --- /dev/null +++ b/library/core/src/slice/sort/unstable/heapsort.rs @@ -0,0 +1,79 @@ +//! This module contains a branchless heapsort as fallback for unstable quicksort. + +use crate::{intrinsics, ptr}; + +/// Sorts `v` using heapsort, which guarantees *O*(*n* \* log(*n*)) worst-case. +/// +/// Never inline this, it sits the main hot-loop in `recurse` and is meant as unlikely algorithmic +/// fallback. +/// +/// SAFETY: The caller has to guarantee that `v.len()` >= 2. +#[inline(never)] +pub(crate) unsafe fn heapsort(v: &mut [T], is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + // SAFETY: See function safety. + unsafe { + intrinsics::assume(v.len() >= 2); + + // Build the heap in linear time. + for i in (0..v.len() / 2).rev() { + sift_down(v, i, is_less); + } + + // Pop maximal elements from the heap. + for i in (1..v.len()).rev() { + v.swap(0, i); + sift_down(&mut v[..i], 0, is_less); + } + } +} + +// This binary heap respects the invariant `parent >= child`. +// +// SAFETY: The caller has to guarantee that node < `v.len()`. +#[inline(never)] +unsafe fn sift_down(v: &mut [T], mut node: usize, is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + // SAFETY: See function safety. + unsafe { + intrinsics::assume(node < v.len()); + } + + let len = v.len(); + + let v_base = v.as_mut_ptr(); + + loop { + // Children of `node`. + let mut child = 2 * node + 1; + if child >= len { + break; + } + + // SAFETY: The invariants and checks guarantee that both node and child are in-bounds. + unsafe { + // Choose the greater child. + if child + 1 < len { + // We need a branch to be sure not to out-of-bounds index, + // but it's highly predictable. The comparison, however, + // is better done branchless, especially for primitives. + child += is_less(&*v_base.add(child), &*v_base.add(child + 1)) as usize; + } + + // Stop if the invariant holds at `node`. + if !is_less(&*v_base.add(node), &*v_base.add(child)) { + break; + } + + // Swap `node` with the greater child, move one step down, and continue sifting. This + // could be ptr::swap_nonoverlapping but that adds a significant amount of binary-size. + ptr::swap(v_base.add(node), v_base.add(child)); + } + + node = child; + } +} diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs new file mode 100644 index 0000000000000..ed735e1ebfbc0 --- /dev/null +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -0,0 +1,75 @@ +//! This module contains the entry points for `slice::sort_unstable`. + +use crate::intrinsics; +use crate::mem::SizedTypeProperties; +use crate::slice::sort::shared::find_existing_run; +use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; + +pub(crate) mod heapsort; +pub(crate) mod quicksort; + +/// Unstable sort called ipnsort by Lukas Bergdoll. +/// Design document: +/// +/// +/// Upholds all safety properties outlined here: +/// +#[inline(always)] +pub fn sort bool>(v: &mut [T], is_less: &mut F) { + // Arrays of zero-sized types are always all-equal, and thus sorted. + if T::IS_ZST { + return; + } + + // Instrumenting the standard library showed that 90+% of the calls to sort + // by rustc are either of size 0 or 1. + let len = v.len(); + if intrinsics::likely(len < 2) { + return; + } + + // More advanced sorting methods than insertion sort are faster if called in + // a hot loop for small inputs, but for general-purpose code the small + // binary size of insertion sort is more important. The instruction cache in + // modern processors is very valuable, and for a single sort call in general + // purpose code any gains from an advanced method are cancelled by i-cache + // misses during the sort, and thrashing the i-cache for surrounding code. + const MAX_LEN_ALWAYS_INSERTION_SORT: usize = 20; + if intrinsics::likely(len <= MAX_LEN_ALWAYS_INSERTION_SORT) { + insertion_sort_shift_left(v, 1, is_less); + return; + } + + ipnsort(v, is_less); +} + +/// See [`sort`] +/// +/// Deliberately don't inline the main sorting routine entrypoint to ensure the +/// inlined insertion sort i-cache footprint remains minimal. +#[inline(never)] +fn ipnsort(v: &mut [T], is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + let (run_len, was_reversed) = find_existing_run(v, is_less); + + // SAFETY: find_existing_run promises to return a valid run_len. + unsafe { intrinsics::assume(run_len <= len) }; + + if run_len == len { + if was_reversed { + v.reverse(); + } + + // It would be possible to a do in-place merging here for a long existing streak. But that + // makes the implementation a lot bigger, users can use `slice::sort` for that use-case. + return; + } + + // Limit the number of imbalanced partitions to `2 * floor(log2(len))`. + // The binary OR by one is used to eliminate the zero-check in the logarithm. + let limit = 2 * (len | 1).ilog2(); + crate::slice::sort::unstable::quicksort::quicksort(v, None, limit, is_less); +} diff --git a/library/core/src/slice/sort/unstable/quicksort.rs b/library/core/src/slice/sort/unstable/quicksort.rs new file mode 100644 index 0000000000000..cd53656e9b4b8 --- /dev/null +++ b/library/core/src/slice/sort/unstable/quicksort.rs @@ -0,0 +1,349 @@ +//! This module contains an unstable quicksort and two partition implementations. + +use crate::mem::{self, ManuallyDrop}; +use crate::slice::sort::shared::pivot::choose_pivot; +use crate::slice::sort::shared::smallsort::UnstableSmallSortTypeImpl; +use crate::{intrinsics, ptr}; + +/// Sorts `v` recursively. +/// +/// If the slice had a predecessor in the original array, it is specified as `ancestor_pivot`. +/// +/// `limit` is the number of allowed imbalanced partitions before switching to `heapsort`. If zero, +/// this function will immediately switch to heapsort. +pub(crate) fn quicksort<'a, T, F>( + mut v: &'a mut [T], + mut ancestor_pivot: Option<&'a T>, + mut limit: u32, + is_less: &mut F, +) where + F: FnMut(&T, &T) -> bool, +{ + loop { + if v.len() <= T::small_sort_threshold() { + T::small_sort(v, is_less); + return; + } + + // If too many bad pivot choices were made, simply fall back to heapsort in order to + // guarantee `O(N x log(N))` worst-case. + if limit == 0 { + // SAFETY: We assume the `small_sort` threshold is at least 1. + unsafe { + crate::slice::sort::unstable::heapsort::heapsort(v, is_less); + } + return; + } + + limit -= 1; + + // Choose a pivot and try guessing whether the slice is already sorted. + let pivot_pos = choose_pivot(v, is_less); + + // If the chosen pivot is equal to the predecessor, then it's the smallest element in the + // slice. Partition the slice into elements equal to and elements greater than the pivot. + // This case is usually hit when the slice contains many duplicate elements. + if let Some(p) = ancestor_pivot { + // SAFETY: We assume choose_pivot yields an in-bounds position. + if !is_less(p, unsafe { v.get_unchecked(pivot_pos) }) { + let num_lt = partition(v, pivot_pos, &mut |a, b| !is_less(b, a)); + + // Continue sorting elements greater than the pivot. We know that `num_lt` contains + // the pivot. So we can continue after `num_lt`. + v = &mut v[(num_lt + 1)..]; + ancestor_pivot = None; + continue; + } + } + + // Partition the slice. + let num_lt = partition(v, pivot_pos, is_less); + // SAFETY: partition ensures that `num_lt` will be in-bounds. + unsafe { intrinsics::assume(num_lt < v.len()) }; + + // Split the slice into `left`, `pivot`, and `right`. + let (left, right) = v.split_at_mut(num_lt); + let (pivot, right) = right.split_at_mut(1); + let pivot = &pivot[0]; + + // Recurse into the left side. We have a fixed recursion limit, testing shows no real + // benefit for recursing into the shorter side. + quicksort(left, ancestor_pivot, limit, is_less); + + // Continue with the right side. + v = right; + ancestor_pivot = Some(pivot); + } +} + +/// Takes the input slice `v` and re-arranges elements such that when the call returns normally +/// all elements that compare true for `is_less(elem, pivot)` where `pivot == v[pivot_pos]` are +/// on the left side of `v` followed by the other elements, notionally considered greater or +/// equal to `pivot`. +/// +/// Returns the number of elements that are compared true for `is_less(elem, pivot)`. +/// +/// If `is_less` does not implement a total order the resulting order and return value are +/// unspecified. All original elements will remain in `v` and any possible modifications via +/// interior mutability will be observable. Same is true if `is_less` panics or `v.len()` +/// exceeds `scratch.len()`. +pub(crate) fn partition(v: &mut [T], pivot: usize, is_less: &mut F) -> usize +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + + // Allows for panic-free code-gen by proving this property to the compiler. + if len == 0 { + return 0; + } + + // Allows for panic-free code-gen by proving this property to the compiler. + if pivot >= len { + intrinsics::abort(); + } + + // Place the pivot at the beginning of slice. + v.swap(0, pivot); + let (pivot, v_without_pivot) = v.split_at_mut(1); + + // Assuming that Rust generates noalias LLVM IR we can be sure that a partition function + // signature of the form `(v: &mut [T], pivot: &T)` guarantees that pivot and v can't alias. + // Having this guarantee is crucial for optimizations. It's possible to copy the pivot value + // into a stack value, but this creates issues for types with interior mutability mandating + // a drop guard. + let pivot = &mut pivot[0]; + + // This construct is used to limit the LLVM IR generated, which saves large amounts of + // compile-time by only instantiating the code that is needed. Idea by Frank Steffahn. + let num_lt = (const { inst_partition::() })(v_without_pivot, pivot, is_less); + + // Place the pivot between the two partitions. + v.swap(0, num_lt); + + num_lt +} + +const fn inst_partition bool>() -> fn(&mut [T], &T, &mut F) -> usize { + const MAX_BRANCHLESS_PARTITION_SIZE: usize = 96; + if mem::size_of::() <= MAX_BRANCHLESS_PARTITION_SIZE { + // Specialize for types that are relatively cheap to copy, where branchless optimizations + // have large leverage e.g. `u64` and `String`. + partition_lomuto_branchless_cyclic:: + } else { + partition_hoare_branchy_cyclic:: + } +} + +/// See [`partition`]. +fn partition_hoare_branchy_cyclic(v: &mut [T], pivot: &T, is_less: &mut F) -> usize +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + + if len == 0 { + return 0; + } + + // Optimized for large types that are expensive to move. Not optimized for integers. Optimized + // for small code-gen, assuming that is_less is an expensive operation that generates + // substantial amounts of code or a call. And that copying elements will likely be a call to + // memcpy. Using 2 `ptr::copy_nonoverlapping` has the chance to be faster than + // `ptr::swap_nonoverlapping` because `memcpy` can use wide SIMD based on runtime feature + // detection. Benchmarks support this analysis. + + let mut gap_opt: Option> = None; + + // SAFETY: The left-to-right scanning loop performs a bounds check, where we know that `left >= + // v_base && left < right && right <= v_base.add(len)`. The right-to-left scanning loop performs + // a bounds check ensuring that `right` is in-bounds. We checked that `len` is more than zero, + // which means that unconditional `right = right.sub(1)` is safe to do. The exit check makes + // sure that `left` and `right` never alias, making `ptr::copy_nonoverlapping` safe. The + // drop-guard `gap` ensures that should `is_less` panic we always overwrite the duplicate in the + // input. `gap.pos` stores the previous value of `right` and starts at `right` and so it too is + // in-bounds. We never pass the saved `gap.value` to `is_less` while it is inside the `GapGuard` + // thus any changes via interior mutability will be observed. + unsafe { + let v_base = v.as_mut_ptr(); + + let mut left = v_base; + let mut right = v_base.add(len); + + loop { + // Find the first element greater than the pivot. + while left < right && is_less(&*left, pivot) { + left = left.add(1); + } + + // Find the last element equal to the pivot. + loop { + right = right.sub(1); + if left >= right || is_less(&*right, pivot) { + break; + } + } + + if left >= right { + break; + } + + // Swap the found pair of out-of-order elements via cyclic permutation. + let is_first_swap_pair = gap_opt.is_none(); + + if is_first_swap_pair { + gap_opt = Some(GapGuard { pos: right, value: ManuallyDrop::new(ptr::read(left)) }); + } + + let gap = gap_opt.as_mut().unwrap_unchecked(); + + // Single place where we instantiate ptr::copy_nonoverlapping in the partition. + if !is_first_swap_pair { + ptr::copy_nonoverlapping(left, gap.pos, 1); + } + gap.pos = right; + ptr::copy_nonoverlapping(right, left, 1); + + left = left.add(1); + } + + left.sub_ptr(v_base) + + // `gap_opt` goes out of scope and overwrites the last wrong-side element on the right side + // with the first wrong-side element of the left side that was initially overwritten by the + // first wrong-side element on the right side element. + } +} + +struct PartitionState { + // The current element that is being looked at, scans left to right through slice. + right: *mut T, + // Counts the number of elements that compared less-than, also works around: + // https://github.com/rust-lang/rust/issues/117128 + num_lt: usize, + // Gap guard that tracks the temporary duplicate in the input. + gap: GapGuardRaw, +} + +fn partition_lomuto_branchless_cyclic(v: &mut [T], pivot: &T, is_less: &mut F) -> usize +where + F: FnMut(&T, &T) -> bool, +{ + // Novel partition implementation by Lukas Bergdoll and Orson Peters. Branchless Lomuto + // partition paired with a cyclic permutation. + // https://github.com/Voultapher/sort-research-rs/blob/main/writeup/lomcyc_partition/text.md + + let len = v.len(); + let v_base = v.as_mut_ptr(); + + if len == 0 { + return 0; + } + + // SAFETY: We checked that `len` is more than zero, which means that reading `v_base` is safe to + // do. From there we have a bounded loop where `v_base.add(i)` is guaranteed in-bounds. `v` and + // `pivot` can't alias because of type system rules. The drop-guard `gap` ensures that should + // `is_less` panic we always overwrite the duplicate in the input. `gap.pos` stores the previous + // value of `right` and starts at `v_base` and so it too is in-bounds. Given `UNROLL_LEN == 2` + // after the main loop we either have A) the last element in `v` that has not yet been processed + // because `len % 2 != 0`, or B) all elements have been processed except the gap value that was + // saved at the beginning with `ptr::read(v_base)`. In the case A) the loop will iterate twice, + // first performing loop_body to take care of the last element that didn't fit into the unroll. + // After that the behavior is the same as for B) where we use the saved value as `right` to + // overwrite the duplicate. If this very last call to `is_less` panics the saved value will be + // copied back including all possible changes via interior mutability. If `is_less` does not + // panic and the code continues we overwrite the duplicate and do `right = right.add(1)`, this + // is safe to do with `&mut *gap.value` because `T` is the same as `[T; 1]` and generating a + // pointer one past the allocation is safe. + unsafe { + let mut loop_body = |state: &mut PartitionState| { + let right_is_lt = is_less(&*state.right, pivot); + let left = v_base.add(state.num_lt); + + ptr::copy(left, state.gap.pos, 1); + ptr::copy_nonoverlapping(state.right, left, 1); + + state.gap.pos = state.right; + state.num_lt += right_is_lt as usize; + + state.right = state.right.add(1); + }; + + // Ideally we could just use GapGuard in PartitionState, but the reference that is + // materialized with `&mut state` when calling `loop_body` would create a mutable reference + // to the parent struct that contains the gap value, invalidating the reference pointer + // created from a reference to the gap value in the cleanup loop. This is only an issue + // under Stacked Borrows, Tree Borrows accepts the intuitive code using GapGuard as valid. + let mut gap_value = ManuallyDrop::new(ptr::read(v_base)); + + let mut state = PartitionState { + num_lt: 0, + right: v_base.add(1), + + gap: GapGuardRaw { pos: v_base, value: &mut *gap_value }, + }; + + // Manual unrolling that works well on x86, Arm and with opt-level=s without murdering + // compile-times. Leaving this to the compiler yields ok to bad results. + let unroll_len = const { if mem::size_of::() <= 16 { 2 } else { 1 } }; + + let unroll_end = v_base.add(len - (unroll_len - 1)); + while state.right < unroll_end { + if unroll_len == 2 { + loop_body(&mut state); + loop_body(&mut state); + } else { + loop_body(&mut state); + } + } + + // Single instantiate `loop_body` for both the unroll cleanup and cyclic permutation + // cleanup. Optimizes binary-size and compile-time. + let end = v_base.add(len); + loop { + let is_done = state.right == end; + state.right = if is_done { state.gap.value } else { state.right }; + + loop_body(&mut state); + + if is_done { + mem::forget(state.gap); + break; + } + } + + state.num_lt + } +} + +struct GapGuard { + pos: *mut T, + value: ManuallyDrop, +} + +impl Drop for GapGuard { + fn drop(&mut self) { + // SAFETY: `self` MUST be constructed in a way that makes copying the gap value into + // `self.pos` sound. + unsafe { + ptr::copy_nonoverlapping(&*self.value, self.pos, 1); + } + } +} + +/// Ideally this wouldn't be needed and we could just use the regular GapGuard. +/// See comment in [`partition_lomuto_branchless_cyclic`]. +struct GapGuardRaw { + pos: *mut T, + value: *mut T, +} + +impl Drop for GapGuardRaw { + fn drop(&mut self) { + // SAFETY: `self` MUST be constructed in a way that makes copying the gap value into + // `self.pos` sound. + unsafe { + ptr::copy_nonoverlapping(self.value, self.pos, 1); + } + } +} diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs index b6ffb0a608d05..1956a04829d1d 100644 --- a/library/core/src/str/converts.rs +++ b/library/core/src/str/converts.rs @@ -1,9 +1,8 @@ //! Ways to create a `str` from bytes slice. -use crate::{mem, ptr}; - use super::validations::run_utf8_validation; use super::Utf8Error; +use crate::{mem, ptr}; /// Converts a slice of bytes to a string slice. /// @@ -206,7 +205,7 @@ pub const unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str { unsafe { &mut *(v as *mut [u8] as *mut str) } } -/// Creates an `&str` from a pointer and a length. +/// Creates a `&str` from a pointer and a length. /// /// The pointed-to bytes must be valid UTF-8. /// If this might not be the case, use `str::from_utf8(slice::from_raw_parts(ptr, len))`, @@ -222,10 +221,10 @@ pub const unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str { #[rustc_const_unstable(feature = "str_from_raw_parts", issue = "119206")] pub const unsafe fn from_raw_parts<'a>(ptr: *const u8, len: usize) -> &'a str { // SAFETY: the caller must uphold the safety contract for `from_raw_parts`. - unsafe { &*ptr::from_raw_parts(ptr.cast(), len) } + unsafe { &*ptr::from_raw_parts(ptr, len) } } -/// Creates an `&mut str` from a pointer and a length. +/// Creates a `&mut str` from a pointer and a length. /// /// The pointed-to bytes must be valid UTF-8. /// If this might not be the case, use `str::from_utf8_mut(slice::from_raw_parts_mut(ptr, len))`, @@ -241,5 +240,5 @@ pub const unsafe fn from_raw_parts<'a>(ptr: *const u8, len: usize) -> &'a str { #[rustc_const_unstable(feature = "const_str_from_raw_parts_mut", issue = "119206")] pub const unsafe fn from_raw_parts_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut str { // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. - unsafe { &mut *ptr::from_raw_parts_mut(ptr.cast(), len) } + unsafe { &mut *ptr::from_raw_parts_mut(ptr, len) } } diff --git a/library/core/src/str/count.rs b/library/core/src/str/count.rs index 28567a7e753aa..b5d7aaf05d4bd 100644 --- a/library/core/src/str/count.rs +++ b/library/core/src/str/count.rs @@ -17,6 +17,7 @@ //! Note: Because the term "leading byte" can sometimes be ambiguous (for //! example, it could also refer to the first byte of a slice), we'll often use //! the term "non-continuation byte" to refer to these bytes in the code. + use core::intrinsics::unlikely; const USIZE_SIZE: usize = core::mem::size_of::(); @@ -24,7 +25,7 @@ const UNROLL_INNER: usize = 4; #[inline] pub(super) fn count_chars(s: &str) -> usize { - if s.len() < USIZE_SIZE * UNROLL_INNER { + if cfg!(feature = "optimize_for_size") || s.len() < USIZE_SIZE * UNROLL_INNER { // Avoid entering the optimized implementation for strings where the // difference is not likely to matter, or where it might even be slower. // That said, a ton of thought was not spent on the particular threshold diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index d61f04102e5e5..06f796f9f3ad8 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -1,23 +1,20 @@ //! Iterators for `str` methods. -use crate::char as char_mod; +use super::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; +use super::validations::{next_code_point, next_code_point_reverse}; +use super::{ + from_utf8_unchecked, BytesIsNotEmpty, CharEscapeDebugContinue, CharEscapeDefault, + CharEscapeUnicode, IsAsciiWhitespace, IsNotEmpty, IsWhitespace, LinesMap, UnsafeBytesToStr, +}; use crate::fmt::{self, Write}; -use crate::iter::{Chain, FlatMap, Flatten}; -use crate::iter::{Copied, Filter, FusedIterator, Map, TrustedLen}; -use crate::iter::{TrustedRandomAccess, TrustedRandomAccessNoCoerce}; +use crate::iter::{ + Chain, Copied, Filter, FlatMap, Flatten, FusedIterator, Map, TrustedLen, TrustedRandomAccess, + TrustedRandomAccessNoCoerce, +}; use crate::num::NonZero; use crate::ops::Try; -use crate::option; use crate::slice::{self, Split as SliceSplit}; - -use super::from_utf8_unchecked; -use super::pattern::Pattern; -use super::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher}; -use super::validations::{next_code_point, next_code_point_reverse}; -use super::LinesMap; -use super::{BytesIsNotEmpty, UnsafeBytesToStr}; -use super::{CharEscapeDebugContinue, CharEscapeDefault, CharEscapeUnicode}; -use super::{IsAsciiWhitespace, IsNotEmpty, IsWhitespace}; +use crate::{char as char_mod, option}; /// An iterator over the [`char`]s of a string slice. /// @@ -411,7 +408,7 @@ macro_rules! derive_pattern_clone { (clone $t:ident with |$s:ident| $e:expr) => { impl<'a, P> Clone for $t<'a, P> where - P: Pattern<'a, Searcher: Clone>, + P: Pattern: Clone>, { fn clone(&self) -> Self { let $s = self; @@ -424,7 +421,7 @@ macro_rules! derive_pattern_clone { /// This macro generates two public iterator structs /// wrapping a private internal one that makes use of the `Pattern` API. /// -/// For all patterns `P: Pattern<'a>` the following items will be +/// For all patterns `P: Pattern` the following items will be /// generated (generics omitted): /// /// struct $forward_iterator($internal_iterator); @@ -484,12 +481,12 @@ macro_rules! generate_pattern_iterators { } => { $(#[$forward_iterator_attribute])* $(#[$common_stability_attribute])* - pub struct $forward_iterator<'a, P: Pattern<'a>>(pub(super) $internal_iterator<'a, P>); + pub struct $forward_iterator<'a, P: Pattern>(pub(super) $internal_iterator<'a, P>); $(#[$common_stability_attribute])* impl<'a, P> fmt::Debug for $forward_iterator<'a, P> where - P: Pattern<'a, Searcher: fmt::Debug>, + P: Pattern: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple(stringify!($forward_iterator)) @@ -499,7 +496,7 @@ macro_rules! generate_pattern_iterators { } $(#[$common_stability_attribute])* - impl<'a, P: Pattern<'a>> Iterator for $forward_iterator<'a, P> { + impl<'a, P: Pattern> Iterator for $forward_iterator<'a, P> { type Item = $iterty; #[inline] @@ -511,7 +508,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> Clone for $forward_iterator<'a, P> where - P: Pattern<'a, Searcher: Clone>, + P: Pattern: Clone>, { fn clone(&self) -> Self { $forward_iterator(self.0.clone()) @@ -520,12 +517,12 @@ macro_rules! generate_pattern_iterators { $(#[$reverse_iterator_attribute])* $(#[$common_stability_attribute])* - pub struct $reverse_iterator<'a, P: Pattern<'a>>(pub(super) $internal_iterator<'a, P>); + pub struct $reverse_iterator<'a, P: Pattern>(pub(super) $internal_iterator<'a, P>); $(#[$common_stability_attribute])* impl<'a, P> fmt::Debug for $reverse_iterator<'a, P> where - P: Pattern<'a, Searcher: fmt::Debug>, + P: Pattern: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple(stringify!($reverse_iterator)) @@ -537,7 +534,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> Iterator for $reverse_iterator<'a, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + P: Pattern: ReverseSearcher<'a>>, { type Item = $iterty; @@ -550,7 +547,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> Clone for $reverse_iterator<'a, P> where - P: Pattern<'a, Searcher: Clone>, + P: Pattern: Clone>, { fn clone(&self) -> Self { $reverse_iterator(self.0.clone()) @@ -558,12 +555,12 @@ macro_rules! generate_pattern_iterators { } #[stable(feature = "fused", since = "1.26.0")] - impl<'a, P: Pattern<'a>> FusedIterator for $forward_iterator<'a, P> {} + impl<'a, P: Pattern> FusedIterator for $forward_iterator<'a, P> {} #[stable(feature = "fused", since = "1.26.0")] impl<'a, P> FusedIterator for $reverse_iterator<'a, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + P: Pattern: ReverseSearcher<'a>>, {} generate_pattern_iterators!($($t)* with $(#[$common_stability_attribute])*, @@ -578,7 +575,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> DoubleEndedIterator for $forward_iterator<'a, P> where - P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>, + P: Pattern: DoubleEndedSearcher<'a>>, { #[inline] fn next_back(&mut self) -> Option<$iterty> { @@ -589,7 +586,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> DoubleEndedIterator for $reverse_iterator<'a, P> where - P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>, + P: Pattern: DoubleEndedSearcher<'a>>, { #[inline] fn next_back(&mut self) -> Option<$iterty> { @@ -609,17 +606,17 @@ derive_pattern_clone! { with |s| SplitInternal { matcher: s.matcher.clone(), ..*s } } -pub(super) struct SplitInternal<'a, P: Pattern<'a>> { +pub(super) struct SplitInternal<'a, P: Pattern> { pub(super) start: usize, pub(super) end: usize, - pub(super) matcher: P::Searcher, + pub(super) matcher: P::Searcher<'a>, pub(super) allow_trailing_empty: bool, pub(super) finished: bool, } impl<'a, P> fmt::Debug for SplitInternal<'a, P> where - P: Pattern<'a, Searcher: fmt::Debug>, + P: Pattern: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SplitInternal") @@ -632,7 +629,7 @@ where } } -impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { +impl<'a, P: Pattern> SplitInternal<'a, P> { #[inline] fn get_end(&mut self) -> Option<&'a str> { if !self.finished { @@ -689,7 +686,7 @@ impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { #[inline] fn next_back(&mut self) -> Option<&'a str> where - P::Searcher: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a>, { if self.finished { return None; @@ -726,7 +723,7 @@ impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { #[inline] fn next_back_inclusive(&mut self) -> Option<&'a str> where - P::Searcher: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a>, { if self.finished { return None; @@ -796,7 +793,7 @@ generate_pattern_iterators! { delegate double ended; } -impl<'a, P: Pattern<'a>> Split<'a, P> { +impl<'a, P: Pattern> Split<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -819,7 +816,7 @@ impl<'a, P: Pattern<'a>> Split<'a, P> { } } -impl<'a, P: Pattern<'a>> RSplit<'a, P> { +impl<'a, P: Pattern> RSplit<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -860,7 +857,7 @@ generate_pattern_iterators! { delegate double ended; } -impl<'a, P: Pattern<'a>> SplitTerminator<'a, P> { +impl<'a, P: Pattern> SplitTerminator<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -883,7 +880,7 @@ impl<'a, P: Pattern<'a>> SplitTerminator<'a, P> { } } -impl<'a, P: Pattern<'a>> RSplitTerminator<'a, P> { +impl<'a, P: Pattern> RSplitTerminator<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -911,7 +908,7 @@ derive_pattern_clone! { with |s| SplitNInternal { iter: s.iter.clone(), ..*s } } -pub(super) struct SplitNInternal<'a, P: Pattern<'a>> { +pub(super) struct SplitNInternal<'a, P: Pattern> { pub(super) iter: SplitInternal<'a, P>, /// The number of splits remaining pub(super) count: usize, @@ -919,7 +916,7 @@ pub(super) struct SplitNInternal<'a, P: Pattern<'a>> { impl<'a, P> fmt::Debug for SplitNInternal<'a, P> where - P: Pattern<'a, Searcher: fmt::Debug>, + P: Pattern: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SplitNInternal") @@ -929,7 +926,7 @@ where } } -impl<'a, P: Pattern<'a>> SplitNInternal<'a, P> { +impl<'a, P: Pattern> SplitNInternal<'a, P> { #[inline] fn next(&mut self) -> Option<&'a str> { match self.count { @@ -948,7 +945,7 @@ impl<'a, P: Pattern<'a>> SplitNInternal<'a, P> { #[inline] fn next_back(&mut self) -> Option<&'a str> where - P::Searcher: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a>, { match self.count { 0 => None, @@ -987,7 +984,7 @@ generate_pattern_iterators! { delegate single ended; } -impl<'a, P: Pattern<'a>> SplitN<'a, P> { +impl<'a, P: Pattern> SplitN<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -1010,7 +1007,7 @@ impl<'a, P: Pattern<'a>> SplitN<'a, P> { } } -impl<'a, P: Pattern<'a>> RSplitN<'a, P> { +impl<'a, P: Pattern> RSplitN<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -1038,18 +1035,18 @@ derive_pattern_clone! { with |s| MatchIndicesInternal(s.0.clone()) } -pub(super) struct MatchIndicesInternal<'a, P: Pattern<'a>>(pub(super) P::Searcher); +pub(super) struct MatchIndicesInternal<'a, P: Pattern>(pub(super) P::Searcher<'a>); impl<'a, P> fmt::Debug for MatchIndicesInternal<'a, P> where - P: Pattern<'a, Searcher: fmt::Debug>, + P: Pattern: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("MatchIndicesInternal").field(&self.0).finish() } } -impl<'a, P: Pattern<'a>> MatchIndicesInternal<'a, P> { +impl<'a, P: Pattern> MatchIndicesInternal<'a, P> { #[inline] fn next(&mut self) -> Option<(usize, &'a str)> { self.0 @@ -1061,7 +1058,7 @@ impl<'a, P: Pattern<'a>> MatchIndicesInternal<'a, P> { #[inline] fn next_back(&mut self) -> Option<(usize, &'a str)> where - P::Searcher: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a>, { self.0 .next_match_back() @@ -1093,18 +1090,18 @@ derive_pattern_clone! { with |s| MatchesInternal(s.0.clone()) } -pub(super) struct MatchesInternal<'a, P: Pattern<'a>>(pub(super) P::Searcher); +pub(super) struct MatchesInternal<'a, P: Pattern>(pub(super) P::Searcher<'a>); impl<'a, P> fmt::Debug for MatchesInternal<'a, P> where - P: Pattern<'a, Searcher: fmt::Debug>, + P: Pattern: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("MatchesInternal").field(&self.0).finish() } } -impl<'a, P: Pattern<'a>> MatchesInternal<'a, P> { +impl<'a, P: Pattern> MatchesInternal<'a, P> { #[inline] fn next(&mut self) -> Option<&'a str> { // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. @@ -1117,7 +1114,7 @@ impl<'a, P: Pattern<'a>> MatchesInternal<'a, P> { #[inline] fn next_back(&mut self) -> Option<&'a str> where - P::Searcher: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a>, { // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. self.0.next_match_back().map(|(a, b)| unsafe { @@ -1274,10 +1271,8 @@ pub struct SplitWhitespace<'a> { #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] #[derive(Clone, Debug)] pub struct SplitAsciiWhitespace<'a> { - pub(super) inner: Map< - Filter, BytesIsNotEmpty<'a>>, - UnsafeBytesToStr<'a>, - >, + pub(super) inner: + Map, BytesIsNotEmpty>, UnsafeBytesToStr>, } /// An iterator over the substrings of a string, @@ -1290,7 +1285,7 @@ pub struct SplitAsciiWhitespace<'a> { /// /// [`split_inclusive`]: str::split_inclusive #[stable(feature = "split_inclusive", since = "1.51.0")] -pub struct SplitInclusive<'a, P: Pattern<'a>>(pub(super) SplitInternal<'a, P>); +pub struct SplitInclusive<'a, P: Pattern>(pub(super) SplitInternal<'a, P>); #[stable(feature = "split_whitespace", since = "1.1.0")] impl<'a> Iterator for SplitWhitespace<'a> { @@ -1412,7 +1407,7 @@ impl<'a> SplitAsciiWhitespace<'a> { } #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern<'a>> Iterator for SplitInclusive<'a, P> { +impl<'a, P: Pattern> Iterator for SplitInclusive<'a, P> { type Item = &'a str; #[inline] @@ -1422,7 +1417,7 @@ impl<'a, P: Pattern<'a>> Iterator for SplitInclusive<'a, P> { } #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern<'a, Searcher: fmt::Debug>> fmt::Debug for SplitInclusive<'a, P> { +impl<'a, P: Pattern: fmt::Debug>> fmt::Debug for SplitInclusive<'a, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SplitInclusive").field("0", &self.0).finish() } @@ -1430,14 +1425,14 @@ impl<'a, P: Pattern<'a, Searcher: fmt::Debug>> fmt::Debug for SplitInclusive<'a, // FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern<'a, Searcher: Clone>> Clone for SplitInclusive<'a, P> { +impl<'a, P: Pattern: Clone>> Clone for SplitInclusive<'a, P> { fn clone(&self) -> Self { SplitInclusive(self.0.clone()) } } #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>> DoubleEndedIterator +impl<'a, P: Pattern: DoubleEndedSearcher<'a>>> DoubleEndedIterator for SplitInclusive<'a, P> { #[inline] @@ -1447,9 +1442,9 @@ impl<'a, P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>> DoubleEndedIterator } #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern<'a>> FusedIterator for SplitInclusive<'a, P> {} +impl<'a, P: Pattern> FusedIterator for SplitInclusive<'a, P> {} -impl<'a, P: Pattern<'a>> SplitInclusive<'a, P> { +impl<'a, P: Pattern> SplitInclusive<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. diff --git a/library/core/src/str/lossy.rs b/library/core/src/str/lossy.rs index 51a0777c2d613..3f31107acf050 100644 --- a/library/core/src/str/lossy.rs +++ b/library/core/src/str/lossy.rs @@ -1,10 +1,8 @@ -use crate::fmt; -use crate::fmt::Formatter; -use crate::fmt::Write; -use crate::iter::FusedIterator; - use super::from_utf8_unchecked; use super::validations::utf8_char_width; +use crate::fmt; +use crate::fmt::{Formatter, Write}; +use crate::iter::FusedIterator; impl [u8] { /// Creates an iterator over the contiguous valid UTF-8 ranges of this diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index edda4d1b68703..56517348dc7d2 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -13,73 +13,52 @@ mod iter; mod traits; mod validations; -use self::pattern::Pattern; -use self::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher}; - -use crate::ascii; +use self::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; use crate::char::{self, EscapeDebugExtArgs}; -use crate::mem; +use crate::ops::Range; use crate::slice::{self, SliceIndex}; +use crate::{ascii, mem}; pub mod pattern; mod lossy; -#[stable(feature = "utf8_chunks", since = "1.79.0")] -pub use lossy::{Utf8Chunk, Utf8Chunks}; - +#[unstable(feature = "str_from_raw_parts", issue = "119206")] +pub use converts::{from_raw_parts, from_raw_parts_mut}; #[stable(feature = "rust1", since = "1.0.0")] pub use converts::{from_utf8, from_utf8_unchecked}; - #[stable(feature = "str_mut_extras", since = "1.20.0")] pub use converts::{from_utf8_mut, from_utf8_unchecked_mut}; - -#[unstable(feature = "str_from_raw_parts", issue = "119206")] -pub use converts::{from_raw_parts, from_raw_parts_mut}; - #[stable(feature = "rust1", since = "1.0.0")] pub use error::{ParseBoolError, Utf8Error}; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use traits::FromStr; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use iter::{Bytes, CharIndices, Chars, Lines, SplitWhitespace}; - +#[stable(feature = "encode_utf16", since = "1.8.0")] +pub use iter::EncodeUtf16; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated)] pub use iter::LinesAny; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use iter::{RSplit, RSplitTerminator, Split, SplitTerminator}; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use iter::{RSplitN, SplitN}; - -#[stable(feature = "str_matches", since = "1.2.0")] -pub use iter::{Matches, RMatches}; - -#[stable(feature = "str_match_indices", since = "1.5.0")] -pub use iter::{MatchIndices, RMatchIndices}; - -#[stable(feature = "encode_utf16", since = "1.8.0")] -pub use iter::EncodeUtf16; - -#[stable(feature = "str_escape", since = "1.34.0")] -pub use iter::{EscapeDebug, EscapeDefault, EscapeUnicode}; - #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] pub use iter::SplitAsciiWhitespace; - #[stable(feature = "split_inclusive", since = "1.51.0")] pub use iter::SplitInclusive; - +#[stable(feature = "rust1", since = "1.0.0")] +pub use iter::{Bytes, CharIndices, Chars, Lines, SplitWhitespace}; +#[stable(feature = "str_escape", since = "1.34.0")] +pub use iter::{EscapeDebug, EscapeDefault, EscapeUnicode}; +#[stable(feature = "str_match_indices", since = "1.5.0")] +pub use iter::{MatchIndices, RMatchIndices}; +use iter::{MatchIndicesInternal, MatchesInternal, SplitInternal, SplitNInternal}; +#[stable(feature = "str_matches", since = "1.2.0")] +pub use iter::{Matches, RMatches}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use iter::{RSplit, RSplitTerminator, Split, SplitTerminator}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use iter::{RSplitN, SplitN}; +#[stable(feature = "utf8_chunks", since = "1.79.0")] +pub use lossy::{Utf8Chunk, Utf8Chunks}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use traits::FromStr; #[unstable(feature = "str_internals", issue = "none")] pub use validations::{next_code_point, utf8_char_width}; -use iter::MatchIndicesInternal; -use iter::SplitInternal; -use iter::{MatchesInternal, SplitNInternal}; - #[inline(never)] #[cold] #[track_caller] @@ -591,6 +570,7 @@ impl str { /// Creates a string slice from another string slice, bypassing safety /// checks. + /// /// This is generally not recommended, use with caution! For a safe /// alternative see [`str`] and [`IndexMut`]. /// @@ -622,7 +602,7 @@ impl str { unsafe { &mut *(begin..end).get_unchecked_mut(self) } } - /// Divide one string slice into two at an index. + /// Divides one string slice into two at an index. /// /// The argument, `mid`, should be a byte offset from the start of the /// string. It must also be on the boundary of a UTF-8 code point. @@ -661,7 +641,7 @@ impl str { } } - /// Divide one mutable string slice into two at an index. + /// Divides one mutable string slice into two at an index. /// /// The argument, `mid`, should be a byte offset from the start of the /// string. It must also be on the boundary of a UTF-8 code point. @@ -704,7 +684,7 @@ impl str { } } - /// Divide one string slice into two at an index. + /// Divides one string slice into two at an index. /// /// The argument, `mid`, should be a valid byte offset from the start of the /// string. It must also be on the boundary of a UTF-8 code point. The @@ -732,7 +712,7 @@ impl str { /// ``` #[inline] #[must_use] - #[stable(feature = "split_at_checked", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "split_at_checked", since = "1.80.0")] pub fn split_at_checked(&self, mid: usize) -> Option<(&str, &str)> { // is_char_boundary checks that the index is in [0, .len()] if self.is_char_boundary(mid) { @@ -743,7 +723,7 @@ impl str { } } - /// Divide one mutable string slice into two at an index. + /// Divides one mutable string slice into two at an index. /// /// The argument, `mid`, should be a valid byte offset from the start of the /// string. It must also be on the boundary of a UTF-8 code point. The @@ -772,7 +752,7 @@ impl str { /// ``` #[inline] #[must_use] - #[stable(feature = "split_at_checked", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "split_at_checked", since = "1.80.0")] pub fn split_at_mut_checked(&mut self, mid: usize) -> Option<(&mut str, &mut str)> { // is_char_boundary checks that the index is in [0, .len()] if self.is_char_boundary(mid) { @@ -783,7 +763,7 @@ impl str { } } - /// Divide one string slice into two at an index. + /// Divides one string slice into two at an index. /// /// # Safety /// @@ -911,7 +891,7 @@ impl str { CharIndices { front_offset: 0, iter: self.chars() } } - /// An iterator over the bytes of a string slice. + /// Returns an iterator over the bytes of a string slice. /// /// As a string slice consists of a sequence of bytes, we can iterate /// through a string slice by byte. This method returns such an iterator. @@ -983,7 +963,7 @@ impl str { #[cfg_attr(not(test), rustc_diagnostic_item = "str_split_whitespace")] #[inline] pub fn split_whitespace(&self) -> SplitWhitespace<'_> { - SplitWhitespace { inner: self.split(char::is_whitespace).filter(|s| !s.is_empty()) } + SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } } /// Splits a string slice by ASCII whitespace. @@ -1032,17 +1012,12 @@ impl str { #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] #[inline] pub fn split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> { - let inner = self - .as_bytes() - .split(u8::is_ascii_whitespace) - .filter(|s| !s.is_empty()) - // SAFETY: the byte slice came from a string and was only split - // along character boundaries, so the resulting slices are strings. - .map(|bytes| unsafe { from_utf8_unchecked(bytes) }); + let inner = + self.as_bytes().split(IsAsciiWhitespace).filter(BytesIsNotEmpty).map(UnsafeBytesToStr); SplitAsciiWhitespace { inner } } - /// An iterator over the lines of a string, as string slices. + /// Returns an iterator over the lines of a string, as string slices. /// /// Lines are split at line endings that are either newlines (`\n`) or /// sequences of a carriage return followed by a line feed (`\r\n`). @@ -1090,14 +1065,10 @@ impl str { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn lines(&self) -> Lines<'_> { - Lines(self.split_inclusive('\n').map(|line| { - let Some(line) = line.strip_suffix('\n') else { return line }; - let Some(line) = line.strip_suffix('\r') else { return line }; - line - })) + Lines(self.split_inclusive('\n').map(LinesMap)) } - /// An iterator over the lines of a string. + /// Returns an iterator over the lines of a string. #[stable(feature = "rust1", since = "1.0.0")] #[deprecated(since = "1.4.0", note = "use lines() instead now", suggestion = "lines")] #[inline] @@ -1146,7 +1117,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { + pub fn contains(&self, pat: P) -> bool { pat.is_contained_in(self) } @@ -1183,7 +1154,7 @@ impl str { /// assert!(bananas.starts_with(&['a', 'b', 'c', 'd'])); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { + pub fn starts_with(&self, pat: P) -> bool { pat.is_prefix_of(self) } @@ -1207,9 +1178,9 @@ impl str { /// assert!(!bananas.ends_with("nana")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn ends_with<'a, P>(&'a self, pat: P) -> bool + pub fn ends_with(&self, pat: P) -> bool where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { pat.is_suffix_of(self) } @@ -1258,7 +1229,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option { + pub fn find(&self, pat: P) -> Option { pat.into_searcher(self).next_match().map(|(i, _)| i) } @@ -1304,14 +1275,14 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn rfind<'a, P>(&'a self, pat: P) -> Option + pub fn rfind(&self, pat: P) -> Option where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { pat.into_searcher(self).next_match_back().map(|(i, _)| i) } - /// An iterator over substrings of this string slice, separated by + /// Returns an iterator over substrings of this string slice, separated by /// characters matched by a pattern. /// /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a @@ -1426,7 +1397,7 @@ impl str { /// [`split_whitespace`]: str::split_whitespace #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> { + pub fn split(&self, pat: P) -> Split<'_, P> { Split(SplitInternal { start: 0, end: self.len(), @@ -1436,10 +1407,11 @@ impl str { }) } - /// An iterator over substrings of this string slice, separated by - /// characters matched by a pattern. Differs from the iterator produced by - /// `split` in that `split_inclusive` leaves the matched part as the - /// terminator of the substring. + /// Returns an iterator over substrings of this string slice, separated by + /// characters matched by a pattern. + /// + /// Differs from the iterator produced by `split` in that `split_inclusive` + /// leaves the matched part as the terminator of the substring. /// /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. @@ -1466,7 +1438,7 @@ impl str { /// ``` #[stable(feature = "split_inclusive", since = "1.51.0")] #[inline] - pub fn split_inclusive<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitInclusive<'a, P> { + pub fn split_inclusive(&self, pat: P) -> SplitInclusive<'_, P> { SplitInclusive(SplitInternal { start: 0, end: self.len(), @@ -1476,8 +1448,8 @@ impl str { }) } - /// An iterator over substrings of the given string slice, separated by - /// characters matched by a pattern and yielded in reverse order. + /// Returns an iterator over substrings of the given string slice, separated + /// by characters matched by a pattern and yielded in reverse order. /// /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. @@ -1521,15 +1493,15 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn rsplit<'a, P>(&'a self, pat: P) -> RSplit<'a, P> + pub fn rsplit(&self, pat: P) -> RSplit<'_, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { RSplit(self.split(pat).0) } - /// An iterator over substrings of the given string slice, separated by - /// characters matched by a pattern. + /// Returns an iterator over substrings of the given string slice, separated + /// by characters matched by a pattern. /// /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. @@ -1570,11 +1542,11 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> { + pub fn split_terminator(&self, pat: P) -> SplitTerminator<'_, P> { SplitTerminator(SplitInternal { allow_trailing_empty: false, ..self.split(pat).0 }) } - /// An iterator over substrings of `self`, separated by characters + /// Returns an iterator over substrings of `self`, separated by characters /// matched by a pattern and yielded in reverse order. /// /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a @@ -1616,15 +1588,15 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn rsplit_terminator<'a, P>(&'a self, pat: P) -> RSplitTerminator<'a, P> + pub fn rsplit_terminator(&self, pat: P) -> RSplitTerminator<'_, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { RSplitTerminator(self.split_terminator(pat).0) } - /// An iterator over substrings of the given string slice, separated by a - /// pattern, restricted to returning at most `n` items. + /// Returns an iterator over substrings of the given string slice, separated + /// by a pattern, restricted to returning at most `n` items. /// /// If `n` substrings are returned, the last substring (the `n`th substring) /// will contain the remainder of the string. @@ -1671,13 +1643,13 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn splitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> SplitN<'a, P> { + pub fn splitn(&self, n: usize, pat: P) -> SplitN<'_, P> { SplitN(SplitNInternal { iter: self.split(pat).0, count: n }) } - /// An iterator over substrings of this string slice, separated by a - /// pattern, starting from the end of the string, restricted to returning - /// at most `n` items. + /// Returns an iterator over substrings of this string slice, separated by a + /// pattern, starting from the end of the string, restricted to returning at + /// most `n` items. /// /// If `n` substrings are returned, the last substring (the `n`th substring) /// will contain the remainder of the string. @@ -1720,9 +1692,9 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn rsplitn<'a, P>(&'a self, n: usize, pat: P) -> RSplitN<'a, P> + pub fn rsplitn(&self, n: usize, pat: P) -> RSplitN<'_, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { RSplitN(self.splitn(n, pat).0) } @@ -1740,7 +1712,7 @@ impl str { /// ``` #[stable(feature = "str_split_once", since = "1.52.0")] #[inline] - pub fn split_once<'a, P: Pattern<'a>>(&'a self, delimiter: P) -> Option<(&'a str, &'a str)> { + pub fn split_once(&self, delimiter: P) -> Option<(&'_ str, &'_ str)> { let (start, end) = delimiter.into_searcher(self).next_match()?; // SAFETY: `Searcher` is known to return valid indices. unsafe { Some((self.get_unchecked(..start), self.get_unchecked(end..))) } @@ -1758,17 +1730,17 @@ impl str { /// ``` #[stable(feature = "str_split_once", since = "1.52.0")] #[inline] - pub fn rsplit_once<'a, P>(&'a self, delimiter: P) -> Option<(&'a str, &'a str)> + pub fn rsplit_once(&self, delimiter: P) -> Option<(&'_ str, &'_ str)> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { let (start, end) = delimiter.into_searcher(self).next_match_back()?; // SAFETY: `Searcher` is known to return valid indices. unsafe { Some((self.get_unchecked(..start), self.get_unchecked(end..))) } } - /// An iterator over the disjoint matches of a pattern within the given string - /// slice. + /// Returns an iterator over the disjoint matches of a pattern within the + /// given string slice. /// /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. @@ -1798,12 +1770,12 @@ impl str { /// ``` #[stable(feature = "str_matches", since = "1.2.0")] #[inline] - pub fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> { + pub fn matches(&self, pat: P) -> Matches<'_, P> { Matches(MatchesInternal(pat.into_searcher(self))) } - /// An iterator over the disjoint matches of a pattern within this string slice, - /// yielded in reverse order. + /// Returns an iterator over the disjoint matches of a pattern within this + /// string slice, yielded in reverse order. /// /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. @@ -1832,14 +1804,14 @@ impl str { /// ``` #[stable(feature = "str_matches", since = "1.2.0")] #[inline] - pub fn rmatches<'a, P>(&'a self, pat: P) -> RMatches<'a, P> + pub fn rmatches(&self, pat: P) -> RMatches<'_, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { RMatches(self.matches(pat).0) } - /// An iterator over the disjoint matches of a pattern within this string + /// Returns an iterator over the disjoint matches of a pattern within this string /// slice as well as the index that the match starts at. /// /// For matches of `pat` within `self` that overlap, only the indices @@ -1876,11 +1848,11 @@ impl str { /// ``` #[stable(feature = "str_match_indices", since = "1.5.0")] #[inline] - pub fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P> { + pub fn match_indices(&self, pat: P) -> MatchIndices<'_, P> { MatchIndices(MatchIndicesInternal(pat.into_searcher(self))) } - /// An iterator over the disjoint matches of a pattern within `self`, + /// Returns an iterator over the disjoint matches of a pattern within `self`, /// yielded in reverse order along with the index of the match. /// /// For matches of `pat` within `self` that overlap, only the indices @@ -1916,9 +1888,9 @@ impl str { /// ``` #[stable(feature = "str_match_indices", since = "1.5.0")] #[inline] - pub fn rmatch_indices<'a, P>(&'a self, pat: P) -> RMatchIndices<'a, P> + pub fn rmatch_indices(&self, pat: P) -> RMatchIndices<'_, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { RMatchIndices(self.match_indices(pat).0) } @@ -2131,9 +2103,9 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - pub fn trim_matches<'a, P>(&'a self, pat: P) -> &'a str + pub fn trim_matches(&self, pat: P) -> &str where - P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>, + for<'a> P::Searcher<'a>: DoubleEndedSearcher<'a>, { let mut i = 0; let mut j = 0; @@ -2178,7 +2150,7 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] - pub fn trim_start_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { + pub fn trim_start_matches(&self, pat: P) -> &str { let mut i = self.len(); let mut matcher = pat.into_searcher(self); if let Some((a, _)) = matcher.next_reject() { @@ -2211,7 +2183,7 @@ impl str { #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] #[stable(feature = "str_strip", since = "1.45.0")] - pub fn strip_prefix<'a, P: Pattern<'a>>(&'a self, prefix: P) -> Option<&'a str> { + pub fn strip_prefix(&self, prefix: P) -> Option<&str> { prefix.strip_prefix_of(self) } @@ -2238,10 +2210,9 @@ impl str { #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] #[stable(feature = "str_strip", since = "1.45.0")] - pub fn strip_suffix<'a, P>(&'a self, suffix: P) -> Option<&'a str> + pub fn strip_suffix(&self, suffix: P) -> Option<&str> where - P: Pattern<'a>, -

>::Searcher: ReverseSearcher<'a>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { suffix.strip_suffix_of(self) } @@ -2282,9 +2253,9 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] - pub fn trim_end_matches<'a, P>(&'a self, pat: P) -> &'a str + pub fn trim_end_matches(&self, pat: P) -> &str where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { let mut j = 0; let mut matcher = pat.into_searcher(self); @@ -2326,7 +2297,7 @@ impl str { note = "superseded by `trim_start_matches`", suggestion = "trim_start_matches" )] - pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { + pub fn trim_left_matches(&self, pat: P) -> &str { self.trim_start_matches(pat) } @@ -2369,9 +2340,9 @@ impl str { note = "superseded by `trim_end_matches`", suggestion = "trim_end_matches" )] - pub fn trim_right_matches<'a, P>(&'a self, pat: P) -> &'a str + pub fn trim_right_matches(&self, pat: P) -> &str where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { self.trim_end_matches(pat) } @@ -2546,8 +2517,8 @@ impl str { /// ``` #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] - #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] #[inline] pub const fn trim_ascii_start(&self) -> &str { // SAFETY: Removing ASCII characters from a `&str` does not invalidate @@ -2571,8 +2542,8 @@ impl str { /// ``` #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] - #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] #[inline] pub const fn trim_ascii_end(&self) -> &str { // SAFETY: Removing ASCII characters from a `&str` does not invalidate @@ -2597,8 +2568,8 @@ impl str { /// ``` #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] - #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "1.80.0")] #[inline] pub const fn trim_ascii(&self) -> &str { // SAFETY: Removing ASCII characters from a `&str` does not invalidate @@ -2606,7 +2577,7 @@ impl str { unsafe { core::str::from_utf8_unchecked(self.as_bytes().trim_ascii()) } } - /// Return an iterator that escapes each char in `self` with [`char::escape_debug`]. + /// Returns an iterator that escapes each char in `self` with [`char::escape_debug`]. /// /// Note: only extended grapheme codepoints that begin the string will be /// escaped. @@ -2645,22 +2616,17 @@ impl str { #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_debug(&self) -> EscapeDebug<'_> { let mut chars = self.chars(); - let first = chars - .next() - .map(|first| first.escape_debug_ext(EscapeDebugExtArgs::ESCAPE_ALL)) - .into_iter() - .flatten(); - let inner = first.chain(chars.flat_map(|c| { - c.escape_debug_ext(EscapeDebugExtArgs { - escape_grapheme_extended: false, - escape_single_quote: true, - escape_double_quote: true, - }) - })); - EscapeDebug { inner } + EscapeDebug { + inner: chars + .next() + .map(|first| first.escape_debug_ext(EscapeDebugExtArgs::ESCAPE_ALL)) + .into_iter() + .flatten() + .chain(chars.flat_map(CharEscapeDebugContinue)), + } } - /// Return an iterator that escapes each char in `self` with [`char::escape_default`]. + /// Returns an iterator that escapes each char in `self` with [`char::escape_default`]. /// /// # Examples /// @@ -2695,10 +2661,10 @@ impl str { without modifying the original"] #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_default(&self) -> EscapeDefault<'_> { - EscapeDefault { inner: self.chars().flat_map(char::escape_default) } + EscapeDefault { inner: self.chars().flat_map(CharEscapeDefault) } } - /// Return an iterator that escapes each char in `self` with [`char::escape_unicode`]. + /// Returns an iterator that escapes each char in `self` with [`char::escape_unicode`]. /// /// # Examples /// @@ -2733,7 +2699,40 @@ impl str { without modifying the original"] #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_unicode(&self) -> EscapeUnicode<'_> { - EscapeUnicode { inner: self.chars().flat_map(char::escape_unicode) } + EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) } + } + + /// Returns the range that a substring points to. + /// + /// Returns `None` if `substr` does not point within `self`. + /// + /// Unlike [`str::find`], **this does not search through the string**. + /// Instead, it uses pointer arithmetic to find where in the string + /// `substr` is derived from. + /// + /// This is useful for extending [`str::split`] and similar methods. + /// + /// Note that this method may return false positives (typically either + /// `Some(0..0)` or `Some(self.len()..self.len())`) if `substr` is a + /// zero-length `str` that points at the beginning or end of another, + /// independent, `str`. + /// + /// # Examples + /// ``` + /// #![feature(substr_range)] + /// + /// let data = "a, b, b, a"; + /// let mut iter = data.split(", ").map(|s| data.substr_range(s).unwrap()); + /// + /// assert_eq!(iter.next(), Some(0..1)); + /// assert_eq!(iter.next(), Some(3..4)); + /// assert_eq!(iter.next(), Some(6..7)); + /// assert_eq!(iter.next(), Some(9..10)); + /// ``` + #[must_use] + #[unstable(feature = "substr_range", issue = "126769")] + pub fn substr_range(&self, substr: &str) -> Option> { + self.as_bytes().subslice_range(substr.as_bytes()) } } @@ -2764,15 +2763,59 @@ impl Default for &mut str { } } -type LinesMap = impl (Fn(&str) -> &str) + Copy; -type CharEscapeDebugContinue = impl (FnMut(char) -> char::EscapeDebug) + Copy; -type CharEscapeUnicode = impl (Fn(char) -> char::EscapeUnicode) + Copy; -type CharEscapeDefault = impl (Fn(char) -> char::EscapeDefault) + Copy; -type IsWhitespace = impl (Fn(char) -> bool) + Copy; -type IsAsciiWhitespace = impl (Fn(&u8) -> bool) + Copy; -type IsNotEmpty = impl (Fn(&&str) -> bool) + Copy; -type BytesIsNotEmpty<'a> = impl (FnMut(&&'a [u8]) -> bool) + Copy; -type UnsafeBytesToStr<'a> = impl (FnMut(&'a [u8]) -> &'a str) + Copy; +impl_fn_for_zst! { + /// A nameable, cloneable fn type + #[derive(Clone)] + struct LinesMap impl<'a> Fn = |line: &'a str| -> &'a str { + let Some(line) = line.strip_suffix('\n') else { return line }; + let Some(line) = line.strip_suffix('\r') else { return line }; + line + }; + + #[derive(Clone)] + struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug { + c.escape_debug_ext(EscapeDebugExtArgs { + escape_grapheme_extended: false, + escape_single_quote: true, + escape_double_quote: true + }) + }; + + #[derive(Clone)] + struct CharEscapeUnicode impl Fn = |c: char| -> char::EscapeUnicode { + c.escape_unicode() + }; + #[derive(Clone)] + struct CharEscapeDefault impl Fn = |c: char| -> char::EscapeDefault { + c.escape_default() + }; + + #[derive(Clone)] + struct IsWhitespace impl Fn = |c: char| -> bool { + c.is_whitespace() + }; + + #[derive(Clone)] + struct IsAsciiWhitespace impl Fn = |byte: &u8| -> bool { + byte.is_ascii_whitespace() + }; + + #[derive(Clone)] + struct IsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b str| -> bool { + !s.is_empty() + }; + + #[derive(Clone)] + struct BytesIsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b [u8]| -> bool { + !s.is_empty() + }; + + #[derive(Clone)] + struct UnsafeBytesToStr impl<'a> Fn = |bytes: &'a [u8]| -> &'a str { + // SAFETY: not safe + unsafe { from_utf8_unchecked(bytes) } + }; +} // This is required to make `impl From<&str> for Box` and `impl From for Box` not overlap. #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index cc66da25795dd..2f1096db8f00c 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -38,18 +38,17 @@ issue = "27721" )] -use crate::cmp; use crate::cmp::Ordering; use crate::convert::TryInto as _; -use crate::fmt; use crate::slice::memchr; +use crate::{cmp, fmt}; // Pattern /// A string pattern. /// -/// A `Pattern<'a>` expresses that the implementing type -/// can be used as a string pattern for searching in a [`&'a str`][str]. +/// A `Pattern` expresses that the implementing type +/// can be used as a string pattern for searching in a [`&str`][str]. /// /// For example, both `'a'` and `"aa"` are patterns that /// would match at index `1` in the string `"baaaab"`. @@ -97,38 +96,38 @@ use crate::slice::memchr; /// assert_eq!("abcdef_z".find(|ch| ch > 'd' && ch < 'y'), Some(4)); /// assert_eq!("abcddd_z".find(|ch| ch > 'd' && ch < 'y'), None); /// ``` -pub trait Pattern<'a>: Sized { +pub trait Pattern: Sized { /// Associated searcher for this pattern - type Searcher: Searcher<'a>; + type Searcher<'a>: Searcher<'a>; /// Constructs the associated searcher from /// `self` and the `haystack` to search in. - fn into_searcher(self, haystack: &'a str) -> Self::Searcher; + fn into_searcher(self, haystack: &str) -> Self::Searcher<'_>; /// Checks whether the pattern matches anywhere in the haystack #[inline] - fn is_contained_in(self, haystack: &'a str) -> bool { + fn is_contained_in(self, haystack: &str) -> bool { self.into_searcher(haystack).next_match().is_some() } /// Checks whether the pattern matches at the front of the haystack #[inline] - fn is_prefix_of(self, haystack: &'a str) -> bool { + fn is_prefix_of(self, haystack: &str) -> bool { matches!(self.into_searcher(haystack).next(), SearchStep::Match(0, _)) } /// Checks whether the pattern matches at the back of the haystack #[inline] - fn is_suffix_of(self, haystack: &'a str) -> bool + fn is_suffix_of<'a>(self, haystack: &'a str) -> bool where - Self::Searcher: ReverseSearcher<'a>, + Self::Searcher<'a>: ReverseSearcher<'a>, { matches!(self.into_searcher(haystack).next_back(), SearchStep::Match(_, j) if haystack.len() == j) } /// Removes the pattern from the front of haystack, if it matches. #[inline] - fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_prefix_of(self, haystack: &str) -> Option<&str> { if let SearchStep::Match(start, len) = self.into_searcher(haystack).next() { debug_assert_eq!( start, 0, @@ -144,9 +143,9 @@ pub trait Pattern<'a>: Sized { /// Removes the pattern from the back of haystack, if it matches. #[inline] - fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> + fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str> where - Self::Searcher: ReverseSearcher<'a>, + Self::Searcher<'a>: ReverseSearcher<'a>, { if let SearchStep::Match(start, end) = self.into_searcher(haystack).next_back() { debug_assert_eq!( @@ -342,14 +341,14 @@ pub unsafe trait ReverseSearcher<'a>: Searcher<'a> { /// /// `(&str)::Searcher` is not a `DoubleEndedSearcher` because /// the pattern `"aa"` in the haystack `"aaa"` matches as either -/// `"[aa]a"` or `"a[aa]"`, depending from which side it is searched. +/// `"[aa]a"` or `"a[aa]"`, depending on which side it is searched. pub trait DoubleEndedSearcher<'a>: ReverseSearcher<'a> {} ///////////////////////////////////////////////////////////////////////////// // Impl for char ///////////////////////////////////////////////////////////////////////////// -/// Associated type for `>::Searcher`. +/// Associated type for `::Searcher<'a>`. #[derive(Clone, Debug)] pub struct CharSearcher<'a> { haystack: &'a str, @@ -543,11 +542,11 @@ impl<'a> DoubleEndedSearcher<'a> for CharSearcher<'a> {} /// ``` /// assert_eq!("Hello world".find('o'), Some(4)); /// ``` -impl<'a> Pattern<'a> for char { - type Searcher = CharSearcher<'a>; +impl Pattern for char { + type Searcher<'a> = CharSearcher<'a>; #[inline] - fn into_searcher(self, haystack: &'a str) -> Self::Searcher { + fn into_searcher(self, haystack: &str) -> Self::Searcher<'_> { let mut utf8_encoded = [0; 4]; let utf8_size = self .encode_utf8(&mut utf8_encoded) @@ -566,7 +565,7 @@ impl<'a> Pattern<'a> for char { } #[inline] - fn is_contained_in(self, haystack: &'a str) -> bool { + fn is_contained_in(self, haystack: &str) -> bool { if (self as u32) < 128 { haystack.as_bytes().contains(&(self as u8)) } else { @@ -576,27 +575,27 @@ impl<'a> Pattern<'a> for char { } #[inline] - fn is_prefix_of(self, haystack: &'a str) -> bool { + fn is_prefix_of(self, haystack: &str) -> bool { self.encode_utf8(&mut [0u8; 4]).is_prefix_of(haystack) } #[inline] - fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_prefix_of(self, haystack: &str) -> Option<&str> { self.encode_utf8(&mut [0u8; 4]).strip_prefix_of(haystack) } #[inline] - fn is_suffix_of(self, haystack: &'a str) -> bool + fn is_suffix_of<'a>(self, haystack: &'a str) -> bool where - Self::Searcher: ReverseSearcher<'a>, + Self::Searcher<'a>: ReverseSearcher<'a>, { self.encode_utf8(&mut [0u8; 4]).is_suffix_of(haystack) } #[inline] - fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> + fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str> where - Self::Searcher: ReverseSearcher<'a>, + Self::Searcher<'a>: ReverseSearcher<'a>, { self.encode_utf8(&mut [0u8; 4]).strip_suffix_of(haystack) } @@ -651,11 +650,11 @@ struct MultiCharEqSearcher<'a, C: MultiCharEq> { char_indices: super::CharIndices<'a>, } -impl<'a, C: MultiCharEq> Pattern<'a> for MultiCharEqPattern { - type Searcher = MultiCharEqSearcher<'a, C>; +impl Pattern for MultiCharEqPattern { + type Searcher<'a> = MultiCharEqSearcher<'a, C>; #[inline] - fn into_searcher(self, haystack: &'a str) -> MultiCharEqSearcher<'a, C> { + fn into_searcher(self, haystack: &str) -> MultiCharEqSearcher<'_, C> { MultiCharEqSearcher { haystack, char_eq: self.0, char_indices: haystack.char_indices() } } } @@ -710,41 +709,41 @@ impl<'a, C: MultiCharEq> DoubleEndedSearcher<'a> for MultiCharEqSearcher<'a, C> ///////////////////////////////////////////////////////////////////////////// macro_rules! pattern_methods { - ($t:ty, $pmap:expr, $smap:expr) => { - type Searcher = $t; + ($a:lifetime, $t:ty, $pmap:expr, $smap:expr) => { + type Searcher<$a> = $t; #[inline] - fn into_searcher(self, haystack: &'a str) -> $t { + fn into_searcher<$a>(self, haystack: &$a str) -> $t { ($smap)(($pmap)(self).into_searcher(haystack)) } #[inline] - fn is_contained_in(self, haystack: &'a str) -> bool { + fn is_contained_in<$a>(self, haystack: &$a str) -> bool { ($pmap)(self).is_contained_in(haystack) } #[inline] - fn is_prefix_of(self, haystack: &'a str) -> bool { + fn is_prefix_of<$a>(self, haystack: &$a str) -> bool { ($pmap)(self).is_prefix_of(haystack) } #[inline] - fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_prefix_of<$a>(self, haystack: &$a str) -> Option<&$a str> { ($pmap)(self).strip_prefix_of(haystack) } #[inline] - fn is_suffix_of(self, haystack: &'a str) -> bool + fn is_suffix_of<$a>(self, haystack: &$a str) -> bool where - $t: ReverseSearcher<'a>, + $t: ReverseSearcher<$a>, { ($pmap)(self).is_suffix_of(haystack) } #[inline] - fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> + fn strip_suffix_of<$a>(self, haystack: &$a str) -> Option<&$a str> where - $t: ReverseSearcher<'a>, + $t: ReverseSearcher<$a>, { ($pmap)(self).strip_suffix_of(haystack) } @@ -786,16 +785,16 @@ macro_rules! searcher_methods { }; } -/// Associated type for `<[char; N] as Pattern<'a>>::Searcher`. +/// Associated type for `<[char; N] as Pattern>::Searcher<'a>`. #[derive(Clone, Debug)] pub struct CharArraySearcher<'a, const N: usize>( - as Pattern<'a>>::Searcher, + as Pattern>::Searcher<'a>, ); -/// Associated type for `<&[char; N] as Pattern<'a>>::Searcher`. +/// Associated type for `<&[char; N] as Pattern>::Searcher<'a>`. #[derive(Clone, Debug)] pub struct CharArrayRefSearcher<'a, 'b, const N: usize>( - as Pattern<'a>>::Searcher, + as Pattern>::Searcher<'a>, ); /// Searches for chars that are equal to any of the [`char`]s in the array. @@ -806,8 +805,8 @@ pub struct CharArrayRefSearcher<'a, 'b, const N: usize>( /// assert_eq!("Hello world".find(['o', 'l']), Some(2)); /// assert_eq!("Hello world".find(['h', 'w']), Some(6)); /// ``` -impl<'a, const N: usize> Pattern<'a> for [char; N] { - pattern_methods!(CharArraySearcher<'a, N>, MultiCharEqPattern, CharArraySearcher); +impl Pattern for [char; N] { + pattern_methods!('a, CharArraySearcher<'a, N>, MultiCharEqPattern, CharArraySearcher); } unsafe impl<'a, const N: usize> Searcher<'a> for CharArraySearcher<'a, N> { @@ -828,8 +827,8 @@ impl<'a, const N: usize> DoubleEndedSearcher<'a> for CharArraySearcher<'a, N> {} /// assert_eq!("Hello world".find(&['o', 'l']), Some(2)); /// assert_eq!("Hello world".find(&['h', 'w']), Some(6)); /// ``` -impl<'a, 'b, const N: usize> Pattern<'a> for &'b [char; N] { - pattern_methods!(CharArrayRefSearcher<'a, 'b, N>, MultiCharEqPattern, CharArrayRefSearcher); +impl<'b, const N: usize> Pattern for &'b [char; N] { + pattern_methods!('a, CharArrayRefSearcher<'a, 'b, N>, MultiCharEqPattern, CharArrayRefSearcher); } unsafe impl<'a, 'b, const N: usize> Searcher<'a> for CharArrayRefSearcher<'a, 'b, N> { @@ -848,9 +847,9 @@ impl<'a, 'b, const N: usize> DoubleEndedSearcher<'a> for CharArrayRefSearcher<'a // Todo: Change / Remove due to ambiguity in meaning. -/// Associated type for `<&[char] as Pattern<'a>>::Searcher`. +/// Associated type for `<&[char] as Pattern>::Searcher<'a>`. #[derive(Clone, Debug)] -pub struct CharSliceSearcher<'a, 'b>( as Pattern<'a>>::Searcher); +pub struct CharSliceSearcher<'a, 'b>( as Pattern>::Searcher<'a>); unsafe impl<'a, 'b> Searcher<'a> for CharSliceSearcher<'a, 'b> { searcher_methods!(forward); @@ -870,17 +869,17 @@ impl<'a, 'b> DoubleEndedSearcher<'a> for CharSliceSearcher<'a, 'b> {} /// assert_eq!("Hello world".find(&['l', 'l'] as &[_]), Some(2)); /// assert_eq!("Hello world".find(&['l', 'l'][..]), Some(2)); /// ``` -impl<'a, 'b> Pattern<'a> for &'b [char] { - pattern_methods!(CharSliceSearcher<'a, 'b>, MultiCharEqPattern, CharSliceSearcher); +impl<'b> Pattern for &'b [char] { + pattern_methods!('a, CharSliceSearcher<'a, 'b>, MultiCharEqPattern, CharSliceSearcher); } ///////////////////////////////////////////////////////////////////////////// // Impl for F: FnMut(char) -> bool ///////////////////////////////////////////////////////////////////////////// -/// Associated type for `>::Searcher`. +/// Associated type for `::Searcher<'a>`. #[derive(Clone)] -pub struct CharPredicateSearcher<'a, F>( as Pattern<'a>>::Searcher) +pub struct CharPredicateSearcher<'a, F>( as Pattern>::Searcher<'a>) where F: FnMut(char) -> bool; @@ -919,11 +918,11 @@ impl<'a, F> DoubleEndedSearcher<'a> for CharPredicateSearcher<'a, F> where F: Fn /// assert_eq!("Hello world".find(char::is_uppercase), Some(0)); /// assert_eq!("Hello world".find(|c| "aeiou".contains(c)), Some(1)); /// ``` -impl<'a, F> Pattern<'a> for F +impl Pattern for F where F: FnMut(char) -> bool, { - pattern_methods!(CharPredicateSearcher<'a, F>, MultiCharEqPattern, CharPredicateSearcher); + pattern_methods!('a, CharPredicateSearcher<'a, F>, MultiCharEqPattern, CharPredicateSearcher); } ///////////////////////////////////////////////////////////////////////////// @@ -931,8 +930,8 @@ where ///////////////////////////////////////////////////////////////////////////// /// Delegates to the `&str` impl. -impl<'a, 'b, 'c> Pattern<'a> for &'c &'b str { - pattern_methods!(StrSearcher<'a, 'b>, |&s| s, |s| s); +impl<'b, 'c> Pattern for &'c &'b str { + pattern_methods!('a, StrSearcher<'a, 'b>, |&s| s, |s| s); } ///////////////////////////////////////////////////////////////////////////// @@ -949,23 +948,23 @@ impl<'a, 'b, 'c> Pattern<'a> for &'c &'b str { /// ``` /// assert_eq!("Hello world".find("world"), Some(6)); /// ``` -impl<'a, 'b> Pattern<'a> for &'b str { - type Searcher = StrSearcher<'a, 'b>; +impl<'b> Pattern for &'b str { + type Searcher<'a> = StrSearcher<'a, 'b>; #[inline] - fn into_searcher(self, haystack: &'a str) -> StrSearcher<'a, 'b> { + fn into_searcher(self, haystack: &str) -> StrSearcher<'_, 'b> { StrSearcher::new(haystack, self) } /// Checks whether the pattern matches at the front of the haystack. #[inline] - fn is_prefix_of(self, haystack: &'a str) -> bool { + fn is_prefix_of(self, haystack: &str) -> bool { haystack.as_bytes().starts_with(self.as_bytes()) } /// Checks whether the pattern matches anywhere in the haystack #[inline] - fn is_contained_in(self, haystack: &'a str) -> bool { + fn is_contained_in(self, haystack: &str) -> bool { if self.len() == 0 { return true; } @@ -991,7 +990,7 @@ impl<'a, 'b> Pattern<'a> for &'b str { /// Removes the pattern from the front of haystack, if it matches. #[inline] - fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_prefix_of(self, haystack: &str) -> Option<&str> { if self.is_prefix_of(haystack) { // SAFETY: prefix was just verified to exist. unsafe { Some(haystack.get_unchecked(self.as_bytes().len()..)) } @@ -1002,13 +1001,19 @@ impl<'a, 'b> Pattern<'a> for &'b str { /// Checks whether the pattern matches at the back of the haystack. #[inline] - fn is_suffix_of(self, haystack: &'a str) -> bool { + fn is_suffix_of<'a>(self, haystack: &'a str) -> bool + where + Self::Searcher<'a>: ReverseSearcher<'a>, + { haystack.as_bytes().ends_with(self.as_bytes()) } /// Removes the pattern from the back of haystack, if it matches. #[inline] - fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str> + where + Self::Searcher<'a>: ReverseSearcher<'a>, + { if self.is_suffix_of(haystack) { let i = haystack.len() - self.as_bytes().len(); // SAFETY: suffix was just verified to exist. @@ -1024,7 +1029,7 @@ impl<'a, 'b> Pattern<'a> for &'b str { ///////////////////////////////////////////////////////////////////////////// #[derive(Clone, Debug)] -/// Associated type for `<&str as Pattern<'a>>::Searcher`. +/// Associated type for `<&str as Pattern>::Searcher<'a>`. pub struct StrSearcher<'a, 'b> { haystack: &'a str, needle: &'b str, @@ -1753,8 +1758,7 @@ fn simd_contains(needle: &str, haystack: &str) -> Option { use crate::ops::BitAnd; use crate::simd::cmp::SimdPartialEq; - use crate::simd::mask8x16 as Mask; - use crate::simd::u8x16 as Block; + use crate::simd::{mask8x16 as Mask, u8x16 as Block}; let first_probe = needle[0]; let last_byte_offset = needle.len() - 1; diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index ba2d6f644962e..b69c476ae5e53 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -1,13 +1,11 @@ //! Trait implementations for `str`. +use super::ParseBoolError; use crate::cmp::Ordering; use crate::intrinsics::unchecked_sub; -use crate::ops; -use crate::ptr; use crate::slice::SliceIndex; use crate::ub_checks::assert_unsafe_precondition; - -use super::ParseBoolError; +use crate::{ops, ptr, range}; /// Implements ordering of strings. /// @@ -261,6 +259,108 @@ unsafe impl SliceIndex for ops::Range { } } +#[unstable(feature = "new_range_api", issue = "125687")] +unsafe impl SliceIndex for range::Range { + type Output = str; + #[inline] + fn get(self, slice: &str) -> Option<&Self::Output> { + if self.start <= self.end + && slice.is_char_boundary(self.start) + && slice.is_char_boundary(self.end) + { + // SAFETY: just checked that `start` and `end` are on a char boundary, + // and we are passing in a safe reference, so the return value will also be one. + // We also checked char boundaries, so this is valid UTF-8. + Some(unsafe { &*self.get_unchecked(slice) }) + } else { + None + } + } + #[inline] + fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { + if self.start <= self.end + && slice.is_char_boundary(self.start) + && slice.is_char_boundary(self.end) + { + // SAFETY: just checked that `start` and `end` are on a char boundary. + // We know the pointer is unique because we got it from `slice`. + Some(unsafe { &mut *self.get_unchecked_mut(slice) }) + } else { + None + } + } + #[inline] + unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { + let slice = slice as *const [u8]; + + assert_unsafe_precondition!( + // We'd like to check that the bounds are on char boundaries, + // but there's not really a way to do so without reading + // behind the pointer, which has aliasing implications. + // It's also not possible to move this check up to + // `str::get_unchecked` without adding a special function + // to `SliceIndex` just for this. + check_library_ub, + "str::get_unchecked requires that the range is within the string slice", + ( + start: usize = self.start, + end: usize = self.end, + len: usize = slice.len() + ) => end >= start && end <= len, + ); + + // SAFETY: the caller guarantees that `self` is in bounds of `slice` + // which satisfies all the conditions for `add`. + unsafe { + let new_len = unchecked_sub(self.end, self.start); + ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), new_len) as *const str + } + } + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { + let slice = slice as *mut [u8]; + + assert_unsafe_precondition!( + check_library_ub, + "str::get_unchecked_mut requires that the range is within the string slice", + ( + start: usize = self.start, + end: usize = self.end, + len: usize = slice.len() + ) => end >= start && end <= len, + ); + + // SAFETY: see comments for `get_unchecked`. + unsafe { + let new_len = unchecked_sub(self.end, self.start); + ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), new_len) as *mut str + } + } + #[inline] + fn index(self, slice: &str) -> &Self::Output { + let (start, end) = (self.start, self.end); + match self.get(slice) { + Some(s) => s, + None => super::slice_error_fail(slice, start, end), + } + } + #[inline] + fn index_mut(self, slice: &mut str) -> &mut Self::Output { + // is_char_boundary checks that the index is in [0, .len()] + // cannot reuse `get` as above, because of NLL trouble + if self.start <= self.end + && slice.is_char_boundary(self.start) + && slice.is_char_boundary(self.end) + { + // SAFETY: just checked that `start` and `end` are on a char boundary, + // and we are passing in a safe reference, so the return value will also be one. + unsafe { &mut *self.get_unchecked_mut(slice) } + } else { + super::slice_error_fail(slice, self.start, self.end) + } + } +} + /// Implements substring slicing for arbitrary bounds. /// /// Returns a slice of the given string bounded by the byte indices @@ -453,6 +553,61 @@ unsafe impl SliceIndex for ops::RangeFrom { } } +#[unstable(feature = "new_range_api", issue = "125687")] +unsafe impl SliceIndex for range::RangeFrom { + type Output = str; + #[inline] + fn get(self, slice: &str) -> Option<&Self::Output> { + if slice.is_char_boundary(self.start) { + // SAFETY: just checked that `start` is on a char boundary, + // and we are passing in a safe reference, so the return value will also be one. + Some(unsafe { &*self.get_unchecked(slice) }) + } else { + None + } + } + #[inline] + fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { + if slice.is_char_boundary(self.start) { + // SAFETY: just checked that `start` is on a char boundary, + // and we are passing in a safe reference, so the return value will also be one. + Some(unsafe { &mut *self.get_unchecked_mut(slice) }) + } else { + None + } + } + #[inline] + unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { + let len = (slice as *const [u8]).len(); + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { (self.start..len).get_unchecked(slice) } + } + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { + let len = (slice as *mut [u8]).len(); + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { (self.start..len).get_unchecked_mut(slice) } + } + #[inline] + fn index(self, slice: &str) -> &Self::Output { + let (start, end) = (self.start, slice.len()); + match self.get(slice) { + Some(s) => s, + None => super::slice_error_fail(slice, start, end), + } + } + #[inline] + fn index_mut(self, slice: &mut str) -> &mut Self::Output { + if slice.is_char_boundary(self.start) { + // SAFETY: just checked that `start` is on a char boundary, + // and we are passing in a safe reference, so the return value will also be one. + unsafe { &mut *self.get_unchecked_mut(slice) } + } else { + super::slice_error_fail(slice, self.start, slice.len()) + } + } +} + /// Implements substring slicing with syntax `&self[begin ..= end]` or `&mut /// self[begin ..= end]`. /// @@ -507,6 +662,43 @@ unsafe impl SliceIndex for ops::RangeInclusive { } } +#[unstable(feature = "new_range_api", issue = "125687")] +unsafe impl SliceIndex for range::RangeInclusive { + type Output = str; + #[inline] + fn get(self, slice: &str) -> Option<&Self::Output> { + if self.end == usize::MAX { None } else { self.into_slice_range().get(slice) } + } + #[inline] + fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { + if self.end == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } + } + #[inline] + unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { + // SAFETY: the caller must uphold the safety contract for `get_unchecked`. + unsafe { self.into_slice_range().get_unchecked(slice) } + } + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { + // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`. + unsafe { self.into_slice_range().get_unchecked_mut(slice) } + } + #[inline] + fn index(self, slice: &str) -> &Self::Output { + if self.end == usize::MAX { + str_index_overflow_fail(); + } + self.into_slice_range().index(slice) + } + #[inline] + fn index_mut(self, slice: &mut str) -> &mut Self::Output { + if self.end == usize::MAX { + str_index_overflow_fail(); + } + self.into_slice_range().index_mut(slice) + } +} + /// Implements substring slicing with syntax `&self[..= end]` or `&mut /// self[..= end]`. /// diff --git a/library/core/src/str/validations.rs b/library/core/src/str/validations.rs index a11d7fee8af0c..cca8ff74dda8b 100644 --- a/library/core/src/str/validations.rs +++ b/library/core/src/str/validations.rs @@ -1,8 +1,7 @@ //! Operations related to UTF-8 validation. -use crate::mem; - use super::Utf8Error; +use crate::mem; /// Returns the initial codepoint accumulator for the first byte. /// The first byte is special, only want bottom 5 bits for width 2, 4 bits diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 232ec589093d3..495d9191a9f85 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -183,7 +183,7 @@ //! //! let spinlock_clone = Arc::clone(&spinlock); //! -//! let thread = thread::spawn(move|| { +//! let thread = thread::spawn(move || { //! spinlock_clone.store(0, Ordering::Release); //! }); //! @@ -223,12 +223,9 @@ #![allow(clippy::not_unsafe_ptr_arg_deref)] use self::Ordering::*; - use crate::cell::UnsafeCell; -use crate::fmt; -use crate::intrinsics; - use crate::hint::spin_loop; +use crate::{fmt, intrinsics}; // Some architectures don't have byte-sized atomics, which results in LLVM // emulating them using a LL/SC loop. However for AtomicBool we can take @@ -443,8 +440,8 @@ impl AtomicBool { /// /// # Safety /// - /// * `ptr` must be aligned to `align_of::()` (note that on some platforms this can - /// be bigger than `align_of::()`). + /// * `ptr` must be aligned to `align_of::()` (note that this is always true, since + /// `align_of::() == 1`). /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`. /// * You must adhere to the [Memory model for atomic accesses]. In particular, it is not /// allowed to mix atomic and non-atomic accesses, or atomic accesses of different sizes, @@ -481,7 +478,7 @@ impl AtomicBool { unsafe { &mut *(self.v.get() as *mut bool) } } - /// Get atomic access to a `&mut bool`. + /// Gets atomic access to a `&mut bool`. /// /// # Examples /// @@ -503,7 +500,7 @@ impl AtomicBool { unsafe { &mut *(v as *mut bool as *mut Self) } } - /// Get non-atomic access to a `&mut [AtomicBool]` slice. + /// Gets non-atomic access to a `&mut [AtomicBool]` slice. /// /// This is safe because the mutable reference guarantees that no other threads are /// concurrently accessing the atomic data. @@ -537,7 +534,7 @@ impl AtomicBool { unsafe { &mut *(this as *mut [Self] as *mut [bool]) } } - /// Get atomic access to a `&mut [bool]` slice. + /// Gets atomic access to a `&mut [bool]` slice. /// /// # Examples /// @@ -1069,7 +1066,6 @@ impl AtomicBool { /// # Examples /// /// ``` - /// #![feature(atomic_bool_fetch_not)] /// use std::sync::atomic::{AtomicBool, Ordering}; /// /// let foo = AtomicBool::new(true); @@ -1081,7 +1077,7 @@ impl AtomicBool { /// assert_eq!(foo.load(Ordering::SeqCst), true); /// ``` #[inline] - #[unstable(feature = "atomic_bool_fetch_not", issue = "98485")] + #[stable(feature = "atomic_bool_fetch_not", since = "1.81.0")] #[cfg(target_has_atomic = "8")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_not(&self, order: Ordering) -> bool { @@ -1277,7 +1273,7 @@ impl AtomicPtr { self.p.get_mut() } - /// Get atomic access to a pointer. + /// Gets atomic access to a pointer. /// /// # Examples /// @@ -1296,7 +1292,6 @@ impl AtomicPtr { #[cfg(target_has_atomic_equal_alignment = "ptr")] #[unstable(feature = "atomic_from_mut", issue = "76314")] pub fn from_mut(v: &mut *mut T) -> &mut Self { - use crate::mem::align_of; let [] = [(); align_of::>() - align_of::<*mut ()>()]; // SAFETY: // - the mutable reference guarantees unique ownership. @@ -1305,7 +1300,7 @@ impl AtomicPtr { unsafe { &mut *(v as *mut *mut T as *mut Self) } } - /// Get non-atomic access to a `&mut [AtomicPtr]` slice. + /// Gets non-atomic access to a `&mut [AtomicPtr]` slice. /// /// This is safe because the mutable reference guarantees that no other threads are /// concurrently accessing the atomic data. @@ -1345,7 +1340,7 @@ impl AtomicPtr { unsafe { &mut *(this as *mut [Self] as *mut [*mut T]) } } - /// Get atomic access to a slice of pointers. + /// Gets atomic access to a slice of pointers. /// /// # Examples /// @@ -2092,10 +2087,10 @@ impl From<*mut T> for AtomicPtr { } #[allow(unused_macros)] // This macro ends up being unused on some architectures. -macro_rules! if_not_8_bit { - (u8, $($tt:tt)*) => { "" }; - (i8, $($tt:tt)*) => { "" }; - ($_:ident, $($tt:tt)*) => { $($tt)* }; +macro_rules! if_8_bit { + (u8, $( yes = [$($yes:tt)*], )? $( no = [$($no:tt)*], )? ) => { concat!("", $($($yes)*)?) }; + (i8, $( yes = [$($yes:tt)*], )? $( no = [$($no:tt)*], )? ) => { concat!("", $($($yes)*)?) }; + ($_:ident, $( yes = [$($yes:tt)*], )? $( no = [$($no:tt)*], )? ) => { concat!("", $($($no)*)?) }; } #[cfg(target_has_atomic_load_store)] @@ -2117,18 +2112,24 @@ macro_rules! atomic_int { $int_type:ident $atomic_type:ident) => { /// An integer type which can be safely shared between threads. /// - /// This type has the same size and bit validity as the underlying - /// integer type, [` + /// This type has the same + #[doc = if_8_bit!( + $int_type, + yes = ["size, alignment, and bit validity"], + no = ["size and bit validity"], + )] + /// as the underlying integer type, [` #[doc = $s_int_type] /// `]. - #[doc = if_not_8_bit! { + #[doc = if_8_bit! { $int_type, - concat!( + no = [ "However, the alignment of this type is always equal to its ", "size, even on targets where [`", $s_int_type, "`] has a ", "lesser alignment." - ) + ], }] + /// /// For more about the differences between atomic types and /// non-atomic types as well as information about the portability of /// this type, please see the [module-level documentation]. @@ -2221,9 +2222,19 @@ macro_rules! atomic_int { /// /// # Safety /// - #[doc = concat!(" * `ptr` must be aligned to \ - `align_of::<", stringify!($atomic_type), ">()` (note that on some platforms this \ - can be bigger than `align_of::<", stringify!($int_type), ">()`).")] + /// * `ptr` must be aligned to + #[doc = concat!(" `align_of::<", stringify!($atomic_type), ">()`")] + #[doc = if_8_bit!{ + $int_type, + yes = [ + " (note that this is always true, since `align_of::<", + stringify!($atomic_type), ">() == 1`)." + ], + no = [ + " (note that on some platforms this can be bigger than `align_of::<", + stringify!($int_type), ">()`)." + ], + }] /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`. /// * You must adhere to the [Memory model for atomic accesses]. In particular, it is not /// allowed to mix atomic and non-atomic accesses, or atomic accesses of different sizes, @@ -2262,12 +2273,12 @@ macro_rules! atomic_int { #[doc = concat!("Get atomic access to a `&mut ", stringify!($int_type), "`.")] /// - #[doc = if_not_8_bit! { + #[doc = if_8_bit! { $int_type, - concat!( + no = [ "**Note:** This function is only available on targets where `", stringify!($int_type), "` has an alignment of ", $align, " bytes." - ) + ], }] /// /// # Examples @@ -2286,7 +2297,6 @@ macro_rules! atomic_int { #[$cfg_align] #[unstable(feature = "atomic_from_mut", issue = "76314")] pub fn from_mut(v: &mut $int_type) -> &mut Self { - use crate::mem::align_of; let [] = [(); align_of::() - align_of::<$int_type>()]; // SAFETY: // - the mutable reference guarantees unique ownership. @@ -2354,7 +2364,6 @@ macro_rules! atomic_int { #[$cfg_align] #[unstable(feature = "atomic_from_mut", issue = "76314")] pub fn from_mut_slice(v: &mut [$int_type]) -> &mut [Self] { - use crate::mem::align_of; let [] = [(); align_of::() - align_of::<$int_type>()]; // SAFETY: // - the mutable reference guarantees unique ownership. @@ -3753,7 +3762,7 @@ impl fmt::Debug for AtomicPtr { #[stable(feature = "atomic_pointer", since = "1.24.0")] impl fmt::Pointer for AtomicPtr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&self.load(Ordering::SeqCst), f) + fmt::Pointer::fmt(&self.load(Ordering::Relaxed), f) } } diff --git a/library/core/src/sync/exclusive.rs b/library/core/src/sync/exclusive.rs index e8170c13ed263..fbf8dafad1869 100644 --- a/library/core/src/sync/exclusive.rs +++ b/library/core/src/sync/exclusive.rs @@ -114,7 +114,7 @@ impl Exclusive { } impl Exclusive { - /// Get exclusive access to the underlying value. + /// Gets exclusive access to the underlying value. #[unstable(feature = "exclusive_wrapper", issue = "98407")] #[must_use] #[inline] @@ -122,7 +122,7 @@ impl Exclusive { &mut self.inner } - /// Get pinned exclusive access to the underlying value. + /// Gets pinned exclusive access to the underlying value. /// /// `Exclusive` is considered to _structurally pin_ the underlying /// value, which means _unpinned_ `Exclusive`s can produce _unpinned_ diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 3d21b09fa8a02..8ce3eb2ea3921 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -1,12 +1,10 @@ #![stable(feature = "futures_api", since = "1.36.0")] -use crate::mem::transmute; - use crate::any::Any; -use crate::fmt; use crate::marker::PhantomData; +use crate::mem::{transmute, ManuallyDrop}; use crate::panic::AssertUnwindSafe; -use crate::ptr; +use crate::{fmt, ptr}; /// A `RawWaker` allows the implementor of a task executor to create a [`Waker`] /// or a [`LocalWaker`] which provides customized wakeup behavior. @@ -62,7 +60,7 @@ impl RawWaker { RawWaker { data, vtable } } - /// Get the `data` pointer used to create this `RawWaker`. + /// Gets the `data` pointer used to create this `RawWaker`. #[inline] #[must_use] #[unstable(feature = "waker_getters", issue = "96992")] @@ -70,7 +68,7 @@ impl RawWaker { self.data } - /// Get the `vtable` pointer used to create this `RawWaker`. + /// Gets the `vtable` pointer used to create this `RawWaker`. #[inline] #[must_use] #[unstable(feature = "waker_getters", issue = "96992")] @@ -151,7 +149,7 @@ pub struct RawWakerVTable { /// pointer. wake_by_ref: unsafe fn(*const ()), - /// This function gets called when a [`Waker`] gets dropped. + /// This function will be called when a [`Waker`] gets dropped. /// /// The implementation of this function must make sure to release any /// resources that are associated with this instance of a [`RawWaker`] and @@ -204,7 +202,8 @@ impl RawWakerVTable { /// /// # `drop` /// - /// This function gets called when a [`Waker`]/[`LocalWaker`] gets dropped. + /// This function will be called when a [`Waker`]/[`LocalWaker`] gets + /// dropped. /// /// The implementation of this function must make sure to release any /// resources that are associated with this instance of a [`RawWaker`] and @@ -249,9 +248,9 @@ pub struct Context<'a> { } impl<'a> Context<'a> { - /// Create a new `Context` from a [`&Waker`](Waker). + /// Creates a new `Context` from a [`&Waker`](Waker). #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[rustc_const_stable(feature = "const_waker", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] pub const fn from_waker(waker: &'a Waker) -> Self { @@ -262,7 +261,7 @@ impl<'a> Context<'a> { #[inline] #[must_use] #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[rustc_const_stable(feature = "const_waker", since = "CURRENT_RUSTC_VERSION")] pub const fn waker(&self) -> &'a Waker { &self.waker } @@ -270,7 +269,7 @@ impl<'a> Context<'a> { /// Returns a reference to the [`LocalWaker`] for the current task. #[inline] #[unstable(feature = "local_waker", issue = "118959")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[rustc_const_unstable(feature = "local_waker", issue = "118959")] pub const fn local_waker(&self) -> &'a LocalWaker { &self.local_waker } @@ -278,11 +277,11 @@ impl<'a> Context<'a> { /// Returns a reference to the extension data for the current task. #[inline] #[unstable(feature = "context_ext", issue = "123392")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[rustc_const_unstable(feature = "context_ext", issue = "123392")] pub const fn ext(&mut self) -> &mut dyn Any { // FIXME: this field makes Context extra-weird about unwind safety // can we justify AssertUnwindSafe if we stabilize this? do we care? - match &mut *self.ext { + match &mut self.ext.0 { ExtData::Some(data) => *data, ExtData::None(unit) => unit, } @@ -335,10 +334,10 @@ pub struct ContextBuilder<'a> { } impl<'a> ContextBuilder<'a> { - /// Create a ContextBuilder from a Waker. + /// Creates a ContextBuilder from a Waker. #[inline] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] #[unstable(feature = "local_waker", issue = "118959")] + #[rustc_const_stable(feature = "const_waker", since = "CURRENT_RUSTC_VERSION")] pub const fn from_waker(waker: &'a Waker) -> Self { // SAFETY: LocalWaker is just Waker without thread safety let local_waker = unsafe { transmute(waker) }; @@ -351,12 +350,12 @@ impl<'a> ContextBuilder<'a> { } } - /// Create a ContextBuilder from an existing Context. + /// Creates a ContextBuilder from an existing Context. #[inline] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] #[unstable(feature = "context_ext", issue = "123392")] + #[rustc_const_unstable(feature = "context_ext", issue = "123392")] pub const fn from(cx: &'a mut Context<'_>) -> Self { - let ext = match &mut *cx.ext { + let ext = match &mut cx.ext.0 { ExtData::Some(ext) => ExtData::Some(*ext), ExtData::None(()) => ExtData::None(()), }; @@ -369,26 +368,26 @@ impl<'a> ContextBuilder<'a> { } } - /// This method is used to set the value for the waker on `Context`. + /// Sets the value for the waker on `Context`. #[inline] #[unstable(feature = "context_ext", issue = "123392")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[rustc_const_unstable(feature = "context_ext", issue = "123392")] pub const fn waker(self, waker: &'a Waker) -> Self { Self { waker, ..self } } - /// This method is used to set the value for the local waker on `Context`. + /// Sets the value for the local waker on `Context`. #[inline] #[unstable(feature = "local_waker", issue = "118959")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[rustc_const_unstable(feature = "local_waker", issue = "118959")] pub const fn local_waker(self, local_waker: &'a LocalWaker) -> Self { Self { local_waker, ..self } } - /// This method is used to set the value for the extension data on `Context`. + /// Sets the value for the extension data on `Context`. #[inline] #[unstable(feature = "context_ext", issue = "123392")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[rustc_const_unstable(feature = "context_ext", issue = "123392")] pub const fn ext(self, data: &'a mut dyn Any) -> Self { Self { ext: ExtData::Some(data), ..self } } @@ -396,7 +395,7 @@ impl<'a> ContextBuilder<'a> { /// Builds the `Context`. #[inline] #[unstable(feature = "local_waker", issue = "118959")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[rustc_const_stable(feature = "const_waker", since = "CURRENT_RUSTC_VERSION")] pub const fn build(self) -> Context<'a> { let ContextBuilder { waker, local_waker, ext, _marker, _marker2 } = self; Context { waker, local_waker, ext: AssertUnwindSafe(ext), _marker, _marker2 } @@ -429,7 +428,7 @@ impl<'a> ContextBuilder<'a> { /// [`Future::poll()`]: core::future::Future::poll /// [`Poll::Pending`]: core::task::Poll::Pending /// [`Wake`]: ../../alloc/task/trait.Wake.html -#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/66401 +#[repr(transparent)] #[stable(feature = "futures_api", since = "1.36.0")] pub struct Waker { waker: RawWaker, @@ -443,7 +442,7 @@ unsafe impl Send for Waker {} unsafe impl Sync for Waker {} impl Waker { - /// Wake up the task associated with this `Waker`. + /// Wakes up the task associated with this `Waker`. /// /// As long as the executor keeps running and the task is not finished, it is /// guaranteed that each invocation of [`wake()`](Self::wake) (or @@ -465,19 +464,17 @@ impl Waker { pub fn wake(self) { // The actual wakeup call is delegated through a virtual function call // to the implementation which is defined by the executor. - let wake = self.waker.vtable.wake; - let data = self.waker.data; // Don't call `drop` -- the waker will be consumed by `wake`. - crate::mem::forget(self); + let this = ManuallyDrop::new(self); // SAFETY: This is safe because `Waker::from_raw` is the only way // to initialize `wake` and `data` requiring the user to acknowledge // that the contract of `RawWaker` is upheld. - unsafe { (wake)(data) }; + unsafe { (this.waker.vtable.wake)(this.waker.data) }; } - /// Wake up the task associated with this `Waker` without consuming the `Waker`. + /// Wakes up the task associated with this `Waker` without consuming the `Waker`. /// /// This is similar to [`wake()`](Self::wake), but may be slightly less efficient in /// the case where an owned `Waker` is available. This method should be preferred to @@ -524,7 +521,7 @@ impl Waker { #[inline] #[must_use] #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[rustc_const_stable(feature = "const_waker", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn from_raw(waker: RawWaker) -> Waker { Waker { waker } } @@ -558,7 +555,7 @@ impl Waker { WAKER } - /// Get a reference to the underlying [`RawWaker`]. + /// Gets a reference to the underlying [`RawWaker`]. #[inline] #[must_use] #[unstable(feature = "waker_getters", issue = "96992")] @@ -695,7 +692,7 @@ impl fmt::Debug for Waker { /// [`Poll::Pending`]: core::task::Poll::Pending /// [`local_waker`]: core::task::Context::local_waker #[unstable(feature = "local_waker", issue = "118959")] -#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/66401 +#[repr(transparent)] pub struct LocalWaker { waker: RawWaker, } @@ -704,7 +701,7 @@ pub struct LocalWaker { impl Unpin for LocalWaker {} impl LocalWaker { - /// Wake up the task associated with this `LocalWaker`. + /// Wakes up the task associated with this `LocalWaker`. /// /// As long as the executor keeps running and the task is not finished, it is /// guaranteed that each invocation of [`wake()`](Self::wake) (or @@ -726,19 +723,17 @@ impl LocalWaker { pub fn wake(self) { // The actual wakeup call is delegated through a virtual function call // to the implementation which is defined by the executor. - let wake = self.waker.vtable.wake; - let data = self.waker.data; // Don't call `drop` -- the waker will be consumed by `wake`. - crate::mem::forget(self); + let this = ManuallyDrop::new(self); // SAFETY: This is safe because `Waker::from_raw` is the only way // to initialize `wake` and `data` requiring the user to acknowledge // that the contract of `RawWaker` is upheld. - unsafe { (wake)(data) }; + unsafe { (this.waker.vtable.wake)(this.waker.data) }; } - /// Wake up the task associated with this `LocalWaker` without consuming the `LocalWaker`. + /// Wakes up the task associated with this `LocalWaker` without consuming the `LocalWaker`. /// /// This is similar to [`wake()`](Self::wake), but may be slightly less efficient in /// the case where an owned `Waker` is available. This method should be preferred to @@ -777,7 +772,7 @@ impl LocalWaker { #[inline] #[must_use] #[unstable(feature = "local_waker", issue = "118959")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[rustc_const_unstable(feature = "local_waker", issue = "118959")] pub const unsafe fn from_raw(waker: RawWaker) -> LocalWaker { Self { waker } } @@ -812,7 +807,7 @@ impl LocalWaker { WAKER } - /// Get a reference to the underlying [`RawWaker`]. + /// Gets a reference to the underlying [`RawWaker`]. #[inline] #[must_use] #[unstable(feature = "waker_getters", issue = "96992")] diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 88fe29c999749..0390bb59a8984 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -348,7 +348,7 @@ impl Duration { #[inline] pub const fn from_weeks(weeks: u64) -> Duration { if weeks > u64::MAX / (SECS_PER_MINUTE * MINS_PER_HOUR * HOURS_PER_DAY * DAYS_PER_WEEK) { - panic!("overflow in Duration::from_days"); + panic!("overflow in Duration::from_weeks"); } Duration::from_secs(weeks * MINS_PER_HOUR * SECS_PER_MINUTE * HOURS_PER_DAY * DAYS_PER_WEEK) @@ -617,16 +617,15 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` - /// #![feature(duration_abs_diff)] /// use std::time::Duration; /// /// assert_eq!(Duration::new(100, 0).abs_diff(Duration::new(80, 0)), Duration::new(20, 0)); /// assert_eq!(Duration::new(100, 400_000_000).abs_diff(Duration::new(110, 0)), Duration::new(9, 600_000_000)); /// ``` - #[unstable(feature = "duration_abs_diff", issue = "117618")] + #[stable(feature = "duration_abs_diff", since = "1.81.0")] + #[rustc_const_stable(feature = "duration_abs_diff", since = "1.81.0")] + #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -639,8 +638,6 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::time::Duration; /// @@ -699,8 +696,6 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::time::Duration; /// @@ -757,8 +752,6 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::time::Duration; /// @@ -813,8 +806,6 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::time::Duration; /// @@ -842,7 +833,7 @@ impl Duration { /// Returns the number of seconds contained by this `Duration` as `f64`. /// - /// The returned value does include the fractional (nanosecond) part of the duration. + /// The returned value includes the fractional (nanosecond) part of the duration. /// /// # Examples /// ``` @@ -861,7 +852,7 @@ impl Duration { /// Returns the number of seconds contained by this `Duration` as `f32`. /// - /// The returned value does include the fractional (nanosecond) part of the duration. + /// The returned value includes the fractional (nanosecond) part of the duration. /// /// # Examples /// ``` @@ -880,7 +871,7 @@ impl Duration { /// Returns the number of milliseconds contained by this `Duration` as `f64`. /// - /// The returned value does include the fractional (nanosecond) part of the duration. + /// The returned value includes the fractional (nanosecond) part of the duration. /// /// # Examples /// ``` @@ -901,7 +892,7 @@ impl Duration { /// Returns the number of milliseconds contained by this `Duration` as `f32`. /// - /// The returned value does include the fractional (nanosecond) part of the duration. + /// The returned value includes the fractional (nanosecond) part of the duration. /// /// # Examples /// ``` @@ -1036,7 +1027,7 @@ impl Duration { Duration::from_secs_f32(rhs * self.as_secs_f32()) } - /// Divide `Duration` by `f64`. + /// Divides `Duration` by `f64`. /// /// # Panics /// This method will panic if result is negative, overflows `Duration` or not finite. @@ -1057,7 +1048,7 @@ impl Duration { Duration::from_secs_f64(self.as_secs_f64() / rhs) } - /// Divide `Duration` by `f32`. + /// Divides `Duration` by `f32`. /// /// # Panics /// This method will panic if result is negative, overflows `Duration` or not finite. @@ -1080,44 +1071,46 @@ impl Duration { Duration::from_secs_f32(self.as_secs_f32() / rhs) } - /// Divide `Duration` by `Duration` and return `f64`. + /// Divides `Duration` by `Duration` and returns `f64`. /// /// # Examples /// ``` - /// #![feature(div_duration)] /// use std::time::Duration; /// /// let dur1 = Duration::new(2, 700_000_000); /// let dur2 = Duration::new(5, 400_000_000); /// assert_eq!(dur1.div_duration_f64(dur2), 0.5); /// ``` - #[unstable(feature = "div_duration", issue = "63139")] + #[stable(feature = "div_duration", since = "1.80.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] pub const fn div_duration_f64(self, rhs: Duration) -> f64 { - self.as_secs_f64() / rhs.as_secs_f64() + let self_nanos = (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos.0 as f64); + let rhs_nanos = (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos.0 as f64); + self_nanos / rhs_nanos } - /// Divide `Duration` by `Duration` and return `f32`. + /// Divides `Duration` by `Duration` and returns `f32`. /// /// # Examples /// ``` - /// #![feature(div_duration)] /// use std::time::Duration; /// /// let dur1 = Duration::new(2, 700_000_000); /// let dur2 = Duration::new(5, 400_000_000); /// assert_eq!(dur1.div_duration_f32(dur2), 0.5); /// ``` - #[unstable(feature = "div_duration", issue = "63139")] + #[stable(feature = "div_duration", since = "1.80.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] pub const fn div_duration_f32(self, rhs: Duration) -> f32 { - self.as_secs_f32() / rhs.as_secs_f32() + let self_nanos = (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos.0 as f32); + let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos.0 as f32); + self_nanos / rhs_nanos } } diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 8e961d8adc372..65d4d5cf2ce41 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -1,8 +1,7 @@ // See core/src/primitive_docs.rs for documentation. use crate::cmp::Ordering::{self, *}; -use crate::marker::ConstParamTy; -use crate::marker::StructuralPartialEq; +use crate::marker::{ConstParamTy_, StructuralPartialEq, UnsizedConstParamTy}; // Recursive macro for implementing n-ary tuple functions and operations // @@ -49,8 +48,15 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ - #[unstable(feature = "structural_match", issue = "31434")] - impl<$($T: ConstParamTy),+> ConstParamTy for ($($T,)+) + #[unstable(feature = "adt_const_params", issue = "95174")] + impl<$($T: ConstParamTy_),+> ConstParamTy_ for ($($T,)+) + {} + } + + maybe_tuple_doc! { + $($T)+ @ + #[unstable(feature = "unsized_const_params", issue = "95174")] + impl<$($T: UnsizedConstParamTy),+> UnsizedConstParamTy for ($($T,)+) {} } diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs index b864b63c5c661..647b58f2aaa37 100644 --- a/library/core/src/ub_checks.rs +++ b/library/core/src/ub_checks.rs @@ -3,9 +3,11 @@ use crate::intrinsics::{self, const_eval_select}; -/// Check that the preconditions of an unsafe function are followed. The check is enabled at -/// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri -/// checks implemented with this macro for language UB are always ignored. +/// Checks that the preconditions of an unsafe function are followed. +/// +/// The check is enabled at runtime if debug assertions are enabled when the +/// caller is monomorphized. In const-eval/Miri checks implemented with this +/// macro for language UB are always ignored. /// /// This macro should be called as /// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)` @@ -79,7 +81,6 @@ macro_rules! assert_unsafe_precondition { } #[unstable(feature = "ub_checks", issue = "none")] pub use assert_unsafe_precondition; - /// Checking library UB is always enabled when UB-checking is done /// (and we use a reexport so that there is no unnecessary wrapper function). #[unstable(feature = "ub_checks", issue = "none")] diff --git a/library/core/src/unicode/mod.rs b/library/core/src/unicode/mod.rs index e1faa407d54c5..6066aa9921607 100644 --- a/library/core/src/unicode/mod.rs +++ b/library/core/src/unicode/mod.rs @@ -1,6 +1,21 @@ #![unstable(feature = "unicode_internals", issue = "none")] #![allow(missing_docs)] +// for use in alloc, not re-exported in std. +#[rustfmt::skip] +pub use unicode_data::case_ignorable::lookup as Case_Ignorable; +pub use unicode_data::cased::lookup as Cased; +pub use unicode_data::conversions; + +#[rustfmt::skip] +pub(crate) use unicode_data::alphabetic::lookup as Alphabetic; +pub(crate) use unicode_data::cc::lookup as Cc; +pub(crate) use unicode_data::grapheme_extend::lookup as Grapheme_Extend; +pub(crate) use unicode_data::lowercase::lookup as Lowercase; +pub(crate) use unicode_data::n::lookup as N; +pub(crate) use unicode_data::uppercase::lookup as Uppercase; +pub(crate) use unicode_data::white_space::lookup as White_Space; + pub(crate) mod printable; mod unicode_data; @@ -16,16 +31,3 @@ mod unicode_data; /// [Unicode 11.0 or later, Section 3.1 Versions of the Unicode Standard](https://www.unicode.org/versions/Unicode11.0.0/ch03.pdf#page=4). #[stable(feature = "unicode_version", since = "1.45.0")] pub const UNICODE_VERSION: (u8, u8, u8) = unicode_data::UNICODE_VERSION; - -// For use in alloc, not re-exported in std. -pub use unicode_data::{ - case_ignorable::lookup as Case_Ignorable, cased::lookup as Cased, conversions, -}; - -pub(crate) use unicode_data::alphabetic::lookup as Alphabetic; -pub(crate) use unicode_data::cc::lookup as Cc; -pub(crate) use unicode_data::grapheme_extend::lookup as Grapheme_Extend; -pub(crate) use unicode_data::lowercase::lookup as Lowercase; -pub(crate) use unicode_data::n::lookup as N; -pub(crate) use unicode_data::uppercase::lookup as Uppercase; -pub(crate) use unicode_data::white_space::lookup as White_Space; diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs index e7773d138c255..b6d18f1ec3822 100644 --- a/library/core/tests/array.rs +++ b/library/core/tests/array.rs @@ -259,7 +259,8 @@ fn iterator_drops() { #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn array_default_impl_avoids_leaks_on_panic() { - use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + use core::sync::atomic::AtomicUsize; + use core::sync::atomic::Ordering::Relaxed; static COUNTER: AtomicUsize = AtomicUsize::new(0); #[derive(Debug)] struct Bomb(#[allow(dead_code)] usize); diff --git a/library/core/tests/clone.rs b/library/core/tests/clone.rs index 64193e1155890..b7130f16f8795 100644 --- a/library/core/tests/clone.rs +++ b/library/core/tests/clone.rs @@ -1,3 +1,6 @@ +use core::clone::CloneToUninit; +use core::mem::MaybeUninit; + #[test] #[allow(suspicious_double_ref_op)] fn test_borrowed_clone() { @@ -14,3 +17,67 @@ fn test_clone_from() { b.clone_from(&a); assert_eq!(*b, 5); } + +#[test] +fn test_clone_to_uninit_slice_success() { + // Using `String`s to exercise allocation and Drop of the individual elements; + // if something is aliased or double-freed, at least Miri will catch that. + let a: [String; 3] = ["a", "b", "c"].map(String::from); + + let mut storage: MaybeUninit<[String; 3]> = MaybeUninit::uninit(); + let b: [String; 3] = unsafe { + a[..].clone_to_uninit(storage.as_mut_ptr() as *mut [String]); + storage.assume_init() + }; + + assert_eq!(a, b); +} + +#[test] +#[cfg(panic = "unwind")] +fn test_clone_to_uninit_slice_drops_on_panic() { + use core::sync::atomic::AtomicUsize; + use core::sync::atomic::Ordering::Relaxed; + + /// A static counter is OK to use as long as _this one test_ isn't run several times in + /// multiple threads. + static COUNTER: AtomicUsize = AtomicUsize::new(0); + /// Counts how many instances are live, and panics if a fifth one is created + struct CountsDropsAndPanics {} + impl CountsDropsAndPanics { + fn new() -> Self { + COUNTER.fetch_add(1, Relaxed); + Self {} + } + } + impl Clone for CountsDropsAndPanics { + fn clone(&self) -> Self { + if COUNTER.load(Relaxed) == 4 { panic!("intentional panic") } else { Self::new() } + } + } + impl Drop for CountsDropsAndPanics { + fn drop(&mut self) { + COUNTER.fetch_sub(1, Relaxed); + } + } + + let a: [CountsDropsAndPanics; 3] = core::array::from_fn(|_| CountsDropsAndPanics::new()); + assert_eq!(COUNTER.load(Relaxed), 3); + + let panic_payload = std::panic::catch_unwind(|| { + let mut storage: MaybeUninit<[CountsDropsAndPanics; 3]> = MaybeUninit::uninit(); + // This should panic halfway through + unsafe { + a[..].clone_to_uninit(storage.as_mut_ptr() as *mut [CountsDropsAndPanics]); + } + }) + .unwrap_err(); + assert_eq!(panic_payload.downcast().unwrap(), Box::new("intentional panic")); + + // Check for lack of leak, which is what this test is looking for + assert_eq!(COUNTER.load(Relaxed), 3, "leaked during clone!"); + + // Might as well exercise the rest of the drops + drop(a); + assert_eq!(COUNTER.load(Relaxed), 0); +} diff --git a/library/core/tests/cmp.rs b/library/core/tests/cmp.rs index 72fdd490da152..6c4e2146f9148 100644 --- a/library/core/tests/cmp.rs +++ b/library/core/tests/cmp.rs @@ -1,7 +1,5 @@ -use core::cmp::{ - self, - Ordering::{self, *}, -}; +use core::cmp::Ordering::{self, *}; +use core::cmp::{self}; #[test] fn test_int_totalord() { diff --git a/library/core/tests/ffi.rs b/library/core/tests/ffi.rs new file mode 100644 index 0000000000000..2b33fbd95f073 --- /dev/null +++ b/library/core/tests/ffi.rs @@ -0,0 +1 @@ +mod cstr; diff --git a/library/core/tests/ffi/cstr.rs b/library/core/tests/ffi/cstr.rs new file mode 100644 index 0000000000000..9bf4c21a9ab97 --- /dev/null +++ b/library/core/tests/ffi/cstr.rs @@ -0,0 +1,15 @@ +use core::ffi::CStr; + +#[test] +fn compares_as_u8s() { + let a: &CStr = c"Hello!"; // Starts with ascii + let a_bytes: &[u8] = a.to_bytes(); + assert!((..0b1000_0000).contains(&a_bytes[0])); + + let b: &CStr = c"こんにちは!"; // Starts with non ascii + let b_bytes: &[u8] = b.to_bytes(); + assert!((0b1000_0000..).contains(&b_bytes[0])); + + assert_eq!(Ord::cmp(a, b), Ord::cmp(a_bytes, b_bytes)); + assert_eq!(PartialOrd::partial_cmp(a, b), PartialOrd::partial_cmp(a_bytes, b_bytes)); +} diff --git a/library/core/tests/fmt/builders.rs b/library/core/tests/fmt/builders.rs index 487ce46be28d7..2bdc334b7c027 100644 --- a/library/core/tests/fmt/builders.rs +++ b/library/core/tests/fmt/builders.rs @@ -441,7 +441,7 @@ mod debug_map { } } - format!("{Foo:?}"); + let _ = format!("{Foo:?}"); } #[test] @@ -455,7 +455,7 @@ mod debug_map { } } - format!("{Foo:?}"); + let _ = format!("{Foo:?}"); } #[test] @@ -469,7 +469,7 @@ mod debug_map { } } - format!("{Foo:?}"); + let _ = format!("{Foo:?}"); } } diff --git a/library/core/tests/future.rs b/library/core/tests/future.rs index db417256dd01e..93aca72d59082 100644 --- a/library/core/tests/future.rs +++ b/library/core/tests/future.rs @@ -56,7 +56,7 @@ fn test_join() { let y = String::new(); let x = join!(async { - println!("{}", &y); + println!("{y}"); 1 }) .await; diff --git a/library/core/tests/hash/sip.rs b/library/core/tests/hash/sip.rs index 0a67c485c98bb..f79954f916b77 100644 --- a/library/core/tests/hash/sip.rs +++ b/library/core/tests/hash/sip.rs @@ -1,7 +1,6 @@ #![allow(deprecated)] -use core::hash::{Hash, Hasher}; -use core::hash::{SipHasher, SipHasher13}; +use core::hash::{Hash, Hasher, SipHasher, SipHasher13}; use core::{mem, slice}; // Hash just the bytes of the slice, without length prefix diff --git a/library/core/tests/iter/adapters/chain.rs b/library/core/tests/iter/adapters/chain.rs index b2429588de12b..1b2c026ee1eb4 100644 --- a/library/core/tests/iter/adapters/chain.rs +++ b/library/core/tests/iter/adapters/chain.rs @@ -1,7 +1,16 @@ -use super::*; use core::iter::*; use core::num::NonZero; +use super::*; + +#[test] +fn test_chain() { + let xs = [0, 1, 2, 3, 4, 5]; + let ys = [30, 40, 50, 60]; + let expected = [0, 1, 2, 3, 4, 5, 30, 40, 50, 60]; + assert_eq!(Vec::from_iter(chain(xs, ys)), expected); +} + #[test] fn test_iterator_chain() { let xs = [0, 1, 2, 3, 4, 5]; diff --git a/library/core/tests/iter/adapters/filter.rs b/library/core/tests/iter/adapters/filter.rs index a2050d89d8564..167851e33336e 100644 --- a/library/core/tests/iter/adapters/filter.rs +++ b/library/core/tests/iter/adapters/filter.rs @@ -1,4 +1,5 @@ use core::iter::*; +use std::rc::Rc; #[test] fn test_iterator_filter_count() { @@ -50,3 +51,15 @@ fn test_double_ended_filter() { assert_eq!(it.next().unwrap(), &2); assert_eq!(it.next_back(), None); } + +#[test] +fn test_next_chunk_does_not_leak() { + let drop_witness: [_; 5] = std::array::from_fn(|_| Rc::new(())); + + let v = (0..5).map(|i| drop_witness[i].clone()).collect::>(); + let _ = v.into_iter().filter(|_| false).next_chunk::<1>(); + + for ref w in drop_witness { + assert_eq!(Rc::strong_count(w), 1); + } +} diff --git a/library/core/tests/iter/adapters/flatten.rs b/library/core/tests/iter/adapters/flatten.rs index 1f953f2aa0110..66b7b6cb563e9 100644 --- a/library/core/tests/iter/adapters/flatten.rs +++ b/library/core/tests/iter/adapters/flatten.rs @@ -1,8 +1,9 @@ -use super::*; use core::assert_eq; use core::iter::*; use core::num::NonZero; +use super::*; + #[test] fn test_iterator_flatten() { let xs = [0, 3, 6]; diff --git a/library/core/tests/iter/adapters/map_windows.rs b/library/core/tests/iter/adapters/map_windows.rs index 7fb2408f8acb7..01cebc9b27fd8 100644 --- a/library/core/tests/iter/adapters/map_windows.rs +++ b/library/core/tests/iter/adapters/map_windows.rs @@ -1,9 +1,12 @@ -use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; #[cfg(not(panic = "abort"))] mod drop_checks { //! These tests mainly make sure the elements are correctly dropped. - use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}; + + use std::sync::atomic::Ordering::SeqCst; + use std::sync::atomic::{AtomicBool, AtomicUsize}; #[derive(Debug)] struct DropInfo { diff --git a/library/core/tests/iter/adapters/peekable.rs b/library/core/tests/iter/adapters/peekable.rs index c1a1c29b609b7..7f4341b8902c8 100644 --- a/library/core/tests/iter/adapters/peekable.rs +++ b/library/core/tests/iter/adapters/peekable.rs @@ -1,6 +1,7 @@ -use super::*; use core::iter::*; +use super::*; + #[test] fn test_iterator_peekable() { let xs = vec![0, 1, 2, 3, 4, 5]; diff --git a/library/core/tests/iter/adapters/zip.rs b/library/core/tests/iter/adapters/zip.rs index ba54de5822bf7..94b49bac45337 100644 --- a/library/core/tests/iter/adapters/zip.rs +++ b/library/core/tests/iter/adapters/zip.rs @@ -1,6 +1,7 @@ -use super::*; use core::iter::*; +use super::*; + #[test] fn test_zip_nth() { let xs = [0, 1, 2, 4, 5]; @@ -239,8 +240,7 @@ fn test_zip_trusted_random_access_composition() { #[test] #[cfg(panic = "unwind")] fn test_zip_trusted_random_access_next_back_drop() { - use std::panic::catch_unwind; - use std::panic::AssertUnwindSafe; + use std::panic::{catch_unwind, AssertUnwindSafe}; let mut counter = 0; diff --git a/library/core/tests/iter/range.rs b/library/core/tests/iter/range.rs index e31db0732e094..d5d2b8bf2b047 100644 --- a/library/core/tests/iter/range.rs +++ b/library/core/tests/iter/range.rs @@ -1,7 +1,8 @@ -use super::*; use core::ascii::Char as AsciiChar; use core::num::NonZero; +use super::*; + #[test] fn test_range() { assert_eq!((0..5).collect::>(), [0, 1, 2, 3, 4]); diff --git a/library/core/tests/iter/sources.rs b/library/core/tests/iter/sources.rs index a15f3a5148f0a..eb8c80dd08724 100644 --- a/library/core/tests/iter/sources.rs +++ b/library/core/tests/iter/sources.rs @@ -1,6 +1,7 @@ -use super::*; use core::iter::*; +use super::*; + #[test] fn test_repeat() { let mut it = repeat(42); diff --git a/library/core/tests/lazy.rs b/library/core/tests/lazy.rs index 7f7f1f0058801..a3b89f15b3f81 100644 --- a/library/core/tests/lazy.rs +++ b/library/core/tests/lazy.rs @@ -1,7 +1,6 @@ -use core::{ - cell::{Cell, LazyCell, OnceCell}, - sync::atomic::{AtomicUsize, Ordering::SeqCst}, -}; +use core::cell::{Cell, LazyCell, OnceCell}; +use core::sync::atomic::AtomicUsize; +use core::sync::atomic::Ordering::SeqCst; #[test] fn once_cell() { diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 797108a8425de..1e336bf96b8fa 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -1,6 +1,11 @@ +// tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(offset_of_nested))] +#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] +#![cfg_attr(test, feature(cfg_match))] #![feature(alloc_layout_extra)] #![feature(array_chunks)] #![feature(array_ptr_get)] +#![feature(array_try_from_fn)] #![feature(array_windows)] #![feature(ascii_char)] #![feature(ascii_char_variants)] @@ -8,120 +13,110 @@ #![feature(async_iterator)] #![feature(bigint_helper_methods)] #![feature(cell_update)] -#![feature(const_align_offset)] +#![feature(clone_to_uninit)] #![feature(const_align_of_val_raw)] +#![feature(const_align_offset)] +#![feature(const_array_from_ref)] #![feature(const_black_box)] #![feature(const_cell_into_inner)] #![feature(const_hash)] #![feature(const_heap)] #![feature(const_intrinsic_copy)] -#![feature(const_int_from_str)] +#![feature(const_ip)] +#![feature(const_ipv4)] +#![feature(const_ipv6)] +#![feature(const_likely)] #![feature(const_maybe_uninit_as_mut_ptr)] +#![feature(const_mut_refs)] #![feature(const_nonnull_new)] +#![feature(const_option)] +#![feature(const_option_ext)] +#![feature(const_pin)] #![feature(const_pointer_is_aligned)] #![feature(const_ptr_as_ref)] #![feature(const_ptr_write)] +#![feature(const_result)] +#![feature(const_slice_from_ref)] #![feature(const_three_way_compare)] #![feature(const_trait_impl)] -#![feature(const_likely)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] #![feature(dec2flt)] -#![feature(div_duration)] -#![feature(duration_abs_diff)] -#![feature(duration_consts_float)] #![feature(duration_constants)] #![feature(duration_constructors)] +#![feature(duration_consts_float)] +#![feature(error_generic_member_access)] #![feature(exact_size_is_empty)] #![feature(extern_types)] -#![feature(freeze)] +#![feature(float_minimum_maximum)] #![feature(flt2dec)] #![feature(fmt_internals)] -#![feature(float_minimum_maximum)] +#![feature(freeze)] #![feature(future_join)] #![feature(generic_assert_internals)] -#![feature(array_try_from_fn)] +#![feature(get_many_mut)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] -#![feature(try_find)] -#![feature(is_sorted)] -#![feature(layout_for_ptr)] -#![feature(pattern)] -#![feature(sort_internals)] -#![feature(slice_take)] -#![feature(slice_from_ptr_range)] -#![feature(slice_split_once)] -#![feature(split_as_slice)] -#![feature(maybe_uninit_fill)] -#![feature(maybe_uninit_uninit_array)] -#![feature(maybe_uninit_write_slice)] -#![feature(maybe_uninit_uninit_array_transpose)] -#![feature(min_specialization)] -#![feature(noop_waker)] -#![feature(numfmt)] -#![feature(num_midpoint)] -#![feature(offset_of_nested)] -#![feature(isqrt)] -#![feature(step_trait)] -#![feature(str_internals)] -#![feature(std_internals)] -#![feature(test)] -#![feature(trusted_len)] -#![feature(try_blocks)] -#![feature(try_trait_v2)] -#![feature(slice_internals)] -#![feature(slice_partition_dedup)] +#![feature(int_roundings)] #![feature(ip)] +#![feature(is_ascii_octdigit)] +#![feature(isqrt)] #![feature(iter_advance_by)] #![feature(iter_array_chunks)] +#![feature(iter_chain)] #![feature(iter_collect_into)] -#![feature(iter_partition_in_place)] #![feature(iter_intersperse)] #![feature(iter_is_partitioned)] +#![feature(iter_map_windows)] #![feature(iter_next_chunk)] #![feature(iter_order_by)] +#![feature(iter_partition_in_place)] #![feature(iter_repeat_n)] #![feature(iterator_try_collect)] #![feature(iterator_try_reduce)] -#![feature(const_ip)] -#![feature(const_ipv4)] -#![feature(const_ipv6)] -#![feature(const_mut_refs)] -#![feature(const_pin)] -#![feature(const_waker)] +#![feature(layout_for_ptr)] +#![feature(maybe_uninit_fill)] +#![feature(maybe_uninit_uninit_array_transpose)] +#![feature(maybe_uninit_write_slice)] +#![feature(min_specialization)] #![feature(never_type)] -#![feature(unwrap_infallible)] +#![feature(noop_waker)] +#![feature(num_midpoint)] +#![feature(numfmt)] +#![feature(pattern)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(ptr_metadata)] -#![feature(lazy_cell)] -#![feature(unsized_tuple_coercion)] -#![feature(const_option)] -#![feature(const_option_ext)] -#![feature(const_result)] -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] -#![cfg_attr(test, feature(cfg_match))] -#![feature(int_roundings)] +#![feature(slice_from_ptr_range)] +#![feature(slice_internals)] +#![feature(slice_partition_dedup)] +#![feature(slice_split_once)] +#![feature(slice_take)] #![feature(split_array)] +#![feature(split_as_slice)] +#![feature(std_internals)] +#![feature(step_trait)] +#![feature(str_internals)] #![feature(strict_provenance)] #![feature(strict_provenance_atomic_ptr)] +#![feature(test)] +#![feature(trait_upcasting)] +#![feature(trusted_len)] #![feature(trusted_random_access)] +#![feature(try_blocks)] +#![feature(try_find)] +#![feature(try_trait_v2)] +#![feature(unsigned_is_multiple_of)] #![feature(unsize)] -#![feature(const_array_from_ref)] -#![feature(const_slice_from_ref)] +#![feature(unsized_tuple_coercion)] +#![feature(unwrap_infallible)] #![feature(waker_getters)] -#![feature(slice_flatten)] -#![feature(error_generic_member_access)] -#![feature(error_in_core)] -#![feature(trait_upcasting)] -#![feature(is_ascii_octdigit)] -#![feature(get_many_mut)] -#![feature(iter_map_windows)] +// tidy-alphabetical-end #![allow(internal_features)] -#![deny(unsafe_op_in_unsafe_fn)] #![deny(fuzzy_provenance_casts)] +#![deny(unsafe_op_in_unsafe_fn)] mod alloc; mod any; @@ -137,6 +132,7 @@ mod clone; mod cmp; mod const_ptr; mod convert; +mod ffi; mod fmt; mod future; mod hash; diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index e388800f400df..b7eee10ec3f9c 100644 --- a/library/core/tests/mem.rs +++ b/library/core/tests/mem.rs @@ -1,6 +1,5 @@ use core::mem::*; use core::ptr; - #[cfg(panic = "unwind")] use std::rc::Rc; @@ -83,12 +82,12 @@ fn align_of_val_raw_packed() { f: [u32], } let storage = [0u8; 4]; - let b: *const B = ptr::from_raw_parts(storage.as_ptr().cast(), 1); + let b: *const B = ptr::from_raw_parts(storage.as_ptr(), 1); assert_eq!(unsafe { align_of_val_raw(b) }, 1); const ALIGN_OF_VAL_RAW: usize = { let storage = [0u8; 4]; - let b: *const B = ptr::from_raw_parts(storage.as_ptr().cast(), 1); + let b: *const B = ptr::from_raw_parts(storage.as_ptr(), 1); unsafe { align_of_val_raw(b) } }; assert_eq!(ALIGN_OF_VAL_RAW, 1); diff --git a/library/core/tests/net/ip_addr.rs b/library/core/tests/net/ip_addr.rs index f9b351ef1980b..a10b51c550d5b 100644 --- a/library/core/tests/net/ip_addr.rs +++ b/library/core/tests/net/ip_addr.rs @@ -1,9 +1,10 @@ -use super::{sa4, sa6}; use core::net::{ IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope, SocketAddr, SocketAddrV4, SocketAddrV6, }; use core::str::FromStr; +use super::{sa4, sa6}; + #[test] fn test_from_str_ipv4() { assert_eq!(Ok(Ipv4Addr::new(127, 0, 0, 1)), "127.0.0.1".parse()); diff --git a/library/core/tests/net/parser.rs b/library/core/tests/net/parser.rs index 36b87d7c1f5e0..e03959ac77c35 100644 --- a/library/core/tests/net/parser.rs +++ b/library/core/tests/net/parser.rs @@ -1,4 +1,5 @@ // FIXME: These tests are all excellent candidates for AFL fuzz testing + use core::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use core::str::FromStr; diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs index 83e2707b57e8b..070a53edc2e0f 100644 --- a/library/core/tests/num/flt2dec/mod.rs +++ b/library/core/tests/num/flt2dec/mod.rs @@ -1,12 +1,10 @@ -use std::mem::MaybeUninit; -use std::{fmt, str}; - -use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded}; -use core::num::flt2dec::{round_up, Sign, MAX_SIG_DIGITS}; use core::num::flt2dec::{ - to_exact_exp_str, to_exact_fixed_str, to_shortest_exp_str, to_shortest_str, + decode, round_up, to_exact_exp_str, to_exact_fixed_str, to_shortest_exp_str, to_shortest_str, + DecodableFloat, Decoded, FullDecoded, Sign, MAX_SIG_DIGITS, }; use core::num::fmt::{Formatted, Part}; +use std::mem::MaybeUninit; +use std::{fmt, str}; mod estimator; mod strategy { diff --git a/library/core/tests/num/flt2dec/random.rs b/library/core/tests/num/flt2dec/random.rs index 0084c1c814e2d..4817a66638391 100644 --- a/library/core/tests/num/flt2dec/random.rs +++ b/library/core/tests/num/flt2dec/random.rs @@ -1,13 +1,10 @@ #![cfg(not(target_arch = "wasm32"))] +use core::num::flt2dec::strategy::grisu::{format_exact_opt, format_shortest_opt}; +use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded, MAX_SIG_DIGITS}; use std::mem::MaybeUninit; use std::str; -use core::num::flt2dec::strategy::grisu::format_exact_opt; -use core::num::flt2dec::strategy::grisu::format_shortest_opt; -use core::num::flt2dec::MAX_SIG_DIGITS; -use core::num::flt2dec::{decode, DecodableFloat, Decoded, FullDecoded}; - use rand::distributions::{Distribution, Uniform}; pub fn decode_finite(v: T) -> Decoded { diff --git a/library/core/tests/num/flt2dec/strategy/dragon.rs b/library/core/tests/num/flt2dec/strategy/dragon.rs index fc2e724a20c7c..be25fee3f6c71 100644 --- a/library/core/tests/num/flt2dec/strategy/dragon.rs +++ b/library/core/tests/num/flt2dec/strategy/dragon.rs @@ -1,7 +1,8 @@ -use super::super::*; use core::num::bignum::Big32x40 as Big; use core::num::flt2dec::strategy::dragon::*; +use super::super::*; + #[test] fn test_mul_pow10() { let mut prevpow10 = Big::from_small(1); diff --git a/library/core/tests/num/flt2dec/strategy/grisu.rs b/library/core/tests/num/flt2dec/strategy/grisu.rs index b59a3b9b72d3b..9b2f0453de73e 100644 --- a/library/core/tests/num/flt2dec/strategy/grisu.rs +++ b/library/core/tests/num/flt2dec/strategy/grisu.rs @@ -1,6 +1,7 @@ -use super::super::*; use core::num::flt2dec::strategy::grisu::*; +use super::super::*; + #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn test_cached_power() { diff --git a/library/core/tests/num/ieee754.rs b/library/core/tests/num/ieee754.rs index 48ab75b6f17a5..b0f6a7545aa93 100644 --- a/library/core/tests/num/ieee754.rs +++ b/library/core/tests/num/ieee754.rs @@ -27,6 +27,7 @@ //! standard. That is why they accept wildly diverse inputs or may seem to duplicate other tests. //! Please consider this carefully when adding, removing, or reorganizing these tests. They are //! here so that it is clear what tests are required by the standard and what can be changed. + use ::core::str::FromStr; // IEEE 754 for many tests is applied to specific bit patterns. diff --git a/library/core/tests/num/int_log.rs b/library/core/tests/num/int_log.rs index a1edb1a518632..2320a7acc35ac 100644 --- a/library/core/tests/num/int_log.rs +++ b/library/core/tests/num/int_log.rs @@ -24,15 +24,15 @@ fn checked_ilog() { #[cfg(not(miri))] // Miri is too slow for i in i16::MIN..=0 { - assert_eq!(i.checked_ilog(4), None); + assert_eq!(i.checked_ilog(4), None, "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=i16::MAX { - assert_eq!(i.checked_ilog(13), Some((i as f32).log(13.0) as u32)); + assert_eq!(i.checked_ilog(13), Some((i as f32).log(13.0) as u32), "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=u16::MAX { - assert_eq!(i.checked_ilog(13), Some((i as f32).log(13.0) as u32)); + assert_eq!(i.checked_ilog(13), Some((i as f32).log(13.0) as u32), "checking {i}"); } } @@ -49,30 +49,30 @@ fn checked_ilog2() { assert_eq!(0i16.checked_ilog2(), None); for i in 1..=u8::MAX { - assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32)); + assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=u16::MAX { // Guard against Android's imprecise f32::ilog2 implementation. if i != 8192 && i != 32768 { - assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32)); + assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}"); } } for i in i8::MIN..=0 { - assert_eq!(i.checked_ilog2(), None); + assert_eq!(i.checked_ilog2(), None, "checking {i}"); } for i in 1..=i8::MAX { - assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32)); + assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in i16::MIN..=0 { - assert_eq!(i.checked_ilog2(), None); + assert_eq!(i.checked_ilog2(), None, "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=i16::MAX { // Guard against Android's imprecise f32::ilog2 implementation. if i != 8192 { - assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32)); + assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}"); } } } @@ -95,19 +95,19 @@ fn checked_ilog10() { #[cfg(not(miri))] // Miri is too slow for i in i16::MIN..=0 { - assert_eq!(i.checked_ilog10(), None); + assert_eq!(i.checked_ilog10(), None, "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=i16::MAX { - assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32)); + assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32), "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=u16::MAX { - assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32)); + assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32), "checking {i}"); } #[cfg(not(miri))] // Miri is too slow for i in 1..=100_000u32 { - assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32)); + assert_eq!(i.checked_ilog10(), Some((i as f32).log10() as u32), "checking {i}"); } } diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs index 0fed854318d54..9d2912c4b22dc 100644 --- a/library/core/tests/num/mod.rs +++ b/library/core/tests/num/mod.rs @@ -729,7 +729,7 @@ assume_usize_width! { } macro_rules! test_float { - ($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr) => { + ($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => { mod $modname { #[test] fn min() { @@ -880,6 +880,27 @@ macro_rules! test_float { assert!(($nan as $fty).midpoint(1.0).is_nan()); assert!((1.0 as $fty).midpoint($nan).is_nan()); assert!(($nan as $fty).midpoint($nan).is_nan()); + + // test if large differences in magnitude are still correctly computed. + // NOTE: that because of how small x and y are, x + y can never overflow + // so (x + y) / 2.0 is always correct + // in particular, `2.pow(i)` will never be at the max exponent, so it could + // be safely doubled, while j is significantly smaller. + for i in $max_exp.saturating_sub(64)..$max_exp { + for j in 0..64u8 { + let large = <$fty>::from(2.0f32).powi(i); + // a much smaller number, such that there is no chance of overflow to test + // potential double rounding in midpoint's implementation. + let small = <$fty>::from(2.0f32).powi($max_exp - 1) + * <$fty>::EPSILON + * <$fty>::from(j); + + let naive = (large + small) / 2.0; + let midpoint = large.midpoint(small); + + assert_eq!(naive, midpoint); + } + } } #[test] fn rem_euclid() { @@ -912,7 +933,8 @@ test_float!( f32::NAN, f32::MIN, f32::MAX, - f32::MIN_POSITIVE + f32::MIN_POSITIVE, + f32::MAX_EXP ); test_float!( f64, @@ -922,5 +944,6 @@ test_float!( f64::NAN, f64::MIN, f64::MAX, - f64::MIN_POSITIVE + f64::MIN_POSITIVE, + f64::MAX_EXP ); diff --git a/library/core/tests/num/uint_macros.rs b/library/core/tests/num/uint_macros.rs index 955440647eb98..d009ad89d5ce7 100644 --- a/library/core/tests/num/uint_macros.rs +++ b/library/core/tests/num/uint_macros.rs @@ -260,6 +260,14 @@ macro_rules! uint_module { assert_eq!(MAX.checked_next_multiple_of(2), None); } + #[test] + fn test_is_next_multiple_of() { + assert!((12 as $T).is_multiple_of(4)); + assert!(!(12 as $T).is_multiple_of(5)); + assert!((0 as $T).is_multiple_of(0)); + assert!(!(12 as $T).is_multiple_of(0)); + } + #[test] fn test_carrying_add() { assert_eq!($T::MAX.carrying_add(1, false), (0, true)); diff --git a/library/core/tests/ops.rs b/library/core/tests/ops.rs index 0c81cba35b3df..2ee0abd399bb6 100644 --- a/library/core/tests/ops.rs +++ b/library/core/tests/ops.rs @@ -1,7 +1,8 @@ mod control_flow; -use core::ops::{Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; -use core::ops::{Deref, DerefMut}; +use core::ops::{ + Bound, Deref, DerefMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, +}; // Test the Range structs and syntax. diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs index b1b9492f182e0..336a79a02ceeb 100644 --- a/library/core/tests/option.rs +++ b/library/core/tests/option.rs @@ -574,4 +574,13 @@ fn as_slice() { assert_eq!(Some(43).as_mut_slice(), &[43]); assert_eq!(None::.as_slice(), &[]); assert_eq!(None::.as_mut_slice(), &[]); + + const A: &[u32] = Some(44).as_slice(); + const B: &[u32] = None.as_slice(); + const _: () = { + let [45] = Some(45).as_mut_slice() else { panic!() }; + let []: &[u32] = None.as_mut_slice() else { panic!() }; + }; + assert_eq!(A, &[44]); + assert_eq!(B, &[]); } diff --git a/library/core/tests/pin_macro.rs b/library/core/tests/pin_macro.rs index 79c8c166c58d9..36c6972515a96 100644 --- a/library/core/tests/pin_macro.rs +++ b/library/core/tests/pin_macro.rs @@ -1,9 +1,8 @@ // edition:2021 -use core::{ - marker::PhantomPinned, - mem::{drop as stuff, transmute}, - pin::{pin, Pin}, -}; + +use core::marker::PhantomPinned; +use core::mem::{drop as stuff, transmute}; +use core::pin::{pin, Pin}; #[test] fn basic() { diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 7b55c2bf8a813..bc1940ebf32b5 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -965,7 +965,7 @@ fn thin_box() { fn value_ptr(&self) -> *const T { let (_, offset) = self.layout(); let data_ptr = unsafe { self.ptr.cast::().as_ptr().add(offset) }; - ptr::from_raw_parts(data_ptr.cast(), self.meta()) + ptr::from_raw_parts(data_ptr, self.meta()) } fn value_mut_ptr(&mut self) -> *mut T { @@ -973,7 +973,7 @@ fn thin_box() { // FIXME: can this line be shared with the same in `value_ptr()` // without upsetting Stacked Borrows? let data_ptr = unsafe { self.ptr.cast::().as_ptr().add(offset) }; - from_raw_parts_mut(data_ptr.cast(), self.meta()) + from_raw_parts_mut(data_ptr, self.meta()) } } @@ -1050,7 +1050,7 @@ fn nonnull_tagged_pointer_with_provenance() { /// A mask for the non-data-carrying bits of the address. pub const ADDRESS_MASK: usize = usize::MAX << Self::NUM_BITS; - /// Create a new tagged pointer from a possibly null pointer. + /// Creates a new tagged pointer from a possibly null pointer. pub fn new(pointer: *mut T) -> Option> { Some(TaggedPointer(NonNull::new(pointer)?)) } @@ -1171,3 +1171,15 @@ fn test_ptr_from_raw_parts_in_const() { assert_eq!(EMPTY_SLICE_PTR.addr(), 123); assert_eq!(EMPTY_SLICE_PTR.len(), 456); } + +#[test] +fn test_ptr_metadata_in_const() { + use std::fmt::Debug; + + const ARRAY_META: () = std::ptr::metadata::<[u16; 3]>(&[1, 2, 3]); + const SLICE_META: usize = std::ptr::metadata::<[u16]>(&[1, 2, 3]); + const DYN_META: DynMetadata = std::ptr::metadata::(&[0_u8; 42]); + assert_eq!(ARRAY_META, ()); + assert_eq!(SLICE_META, 3); + assert_eq!(DYN_META.size_of(), 42); +} diff --git a/library/core/tests/result.rs b/library/core/tests/result.rs index 00a6fd75b4f46..90ec844bc5771 100644 --- a/library/core/tests/result.rs +++ b/library/core/tests/result.rs @@ -410,7 +410,8 @@ fn result_opt_conversions() { #[test] fn result_try_trait_v2_branch() { use core::num::NonZero; - use core::ops::{ControlFlow::*, Try}; + use core::ops::ControlFlow::*; + use core::ops::Try; assert_eq!(Ok::(4).branch(), Continue(4)); assert_eq!(Err::(4).branch(), Break(Err(4))); diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index c91ac2fbb43b0..cdefb5d3eb201 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -69,13 +69,13 @@ fn test_binary_search() { assert_eq!(b.binary_search(&8), Err(5)); let b = [(); usize::MAX]; - assert_eq!(b.binary_search(&()), Ok(usize::MAX / 2)); + assert_eq!(b.binary_search(&()), Ok(usize::MAX - 1)); } #[test] fn test_binary_search_by_overflow() { let b = [(); usize::MAX]; - assert_eq!(b.binary_search_by(|_| Ordering::Equal), Ok(usize::MAX / 2)); + assert_eq!(b.binary_search_by(|_| Ordering::Equal), Ok(usize::MAX - 1)); assert_eq!(b.binary_search_by(|_| Ordering::Greater), Err(0)); assert_eq!(b.binary_search_by(|_| Ordering::Less), Err(usize::MAX)); } @@ -87,13 +87,13 @@ fn test_binary_search_implementation_details() { let b = [1, 1, 2, 2, 3, 3, 3]; assert_eq!(b.binary_search(&1), Ok(1)); assert_eq!(b.binary_search(&2), Ok(3)); - assert_eq!(b.binary_search(&3), Ok(5)); + assert_eq!(b.binary_search(&3), Ok(6)); let b = [1, 1, 1, 1, 1, 3, 3, 3, 3]; assert_eq!(b.binary_search(&1), Ok(4)); - assert_eq!(b.binary_search(&3), Ok(7)); + assert_eq!(b.binary_search(&3), Ok(8)); let b = [1, 1, 1, 1, 3, 3, 3, 3, 3]; - assert_eq!(b.binary_search(&1), Ok(2)); - assert_eq!(b.binary_search(&3), Ok(4)); + assert_eq!(b.binary_search(&1), Ok(3)); + assert_eq!(b.binary_search(&3), Ok(8)); } #[test] @@ -1803,9 +1803,7 @@ fn brute_force_rotate_test_1() { #[test] #[cfg(not(target_arch = "wasm32"))] fn sort_unstable() { - use core::cmp::Ordering::{Equal, Greater, Less}; - use core::slice::heapsort; - use rand::{seq::SliceRandom, Rng}; + use rand::Rng; // Miri is too slow (but still need to `chain` to make the types match) let lens = if cfg!(miri) { (2..20).chain(0..0) } else { (2..25).chain(500..510) }; @@ -1839,31 +1837,10 @@ fn sort_unstable() { tmp.copy_from_slice(v); tmp.sort_unstable_by(|a, b| b.cmp(a)); assert!(tmp.windows(2).all(|w| w[0] >= w[1])); - - // Test heapsort using `<` operator. - tmp.copy_from_slice(v); - heapsort(tmp, |a, b| a < b); - assert!(tmp.windows(2).all(|w| w[0] <= w[1])); - - // Test heapsort using `>` operator. - tmp.copy_from_slice(v); - heapsort(tmp, |a, b| a > b); - assert!(tmp.windows(2).all(|w| w[0] >= w[1])); } } } - // Sort using a completely random comparison function. - // This will reorder the elements *somehow*, but won't panic. - for i in 0..v.len() { - v[i] = i as i32; - } - v.sort_unstable_by(|_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); - v.sort_unstable(); - for i in 0..v.len() { - assert_eq!(v[i], i as i32); - } - // Should not panic. [0i32; 0].sort_unstable(); [(); 10].sort_unstable(); @@ -1879,6 +1856,7 @@ fn sort_unstable() { #[cfg_attr(miri, ignore)] // Miri is too slow fn select_nth_unstable() { use core::cmp::Ordering::{Equal, Greater, Less}; + use rand::seq::SliceRandom; use rand::Rng; diff --git a/library/core/tests/waker.rs b/library/core/tests/waker.rs index 2c66e0d7ad3a4..361e900e69562 100644 --- a/library/core/tests/waker.rs +++ b/library/core/tests/waker.rs @@ -20,3 +20,33 @@ static WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( |_| {}, |_| {}, ); + +// https://github.com/rust-lang/rust/issues/102012#issuecomment-1915282956 +mod nop_waker { + use core::future::{ready, Future}; + use core::pin::Pin; + use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + + const NOP_RAWWAKER: RawWaker = { + fn nop(_: *const ()) {} + const VTAB: RawWakerVTable = RawWakerVTable::new(|_| NOP_RAWWAKER, nop, nop, nop); + RawWaker::new(&() as *const (), &VTAB) + }; + + const NOP_WAKER: &Waker = &unsafe { Waker::from_raw(NOP_RAWWAKER) }; + + const NOP_CONTEXT: Context<'static> = Context::from_waker(NOP_WAKER); + + fn poll_once(f: &mut F) -> Poll + where + F: Future + ?Sized + Unpin, + { + let mut cx = NOP_CONTEXT; + Pin::new(f).as_mut().poll(&mut cx) + } + + #[test] + fn test_const_waker() { + assert_eq!(poll_once(&mut ready(1)), Poll::Ready(1)); + } +} diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index 353de8c5c5743..dc2b42bb90ae8 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -14,7 +14,6 @@ #![feature(std_internals)] #![feature(staged_api)] #![feature(rustc_attrs)] -#![feature(c_unwind)] #![allow(internal_features)] #[cfg(target_os = "android")] diff --git a/library/panic_unwind/Cargo.toml b/library/panic_unwind/Cargo.toml index dce2da3164440..f830808d19648 100644 --- a/library/panic_unwind/Cargo.toml +++ b/library/panic_unwind/Cargo.toml @@ -16,7 +16,7 @@ alloc = { path = "../alloc" } core = { path = "../core" } unwind = { path = "../unwind" } compiler_builtins = "0.1.0" -cfg-if = "1.0" +cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] libc = { version = "0.2", default-features = false } diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs index fed4c52e83c5f..86a43184fb529 100644 --- a/library/panic_unwind/src/emcc.rs +++ b/library/panic_unwind/src/emcc.rs @@ -8,10 +8,9 @@ use alloc::boxed::Box; use core::any::Any; -use core::intrinsics; -use core::mem; -use core::ptr; use core::sync::atomic::{AtomicBool, Ordering}; +use core::{intrinsics, mem, ptr}; + use unwind as uw; // This matches the layout of std::type_info in C++ diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index b0245de501e7e..2d174f4b1a4a2 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -24,7 +24,6 @@ #![feature(rustc_attrs)] #![panic_runtime] #![feature(panic_runtime)] -#![feature(c_unwind)] // `real_imp` is unused with Miri, so silence warnings. #![cfg_attr(miri, allow(dead_code))] #![allow(internal_features)] @@ -36,18 +35,14 @@ use core::panic::PanicPayload; cfg_if::cfg_if! { if #[cfg(target_os = "emscripten")] { #[path = "emcc.rs"] - mod real_imp; + mod imp; } else if #[cfg(target_os = "hermit")] { #[path = "hermit.rs"] - mod real_imp; + mod imp; } else if #[cfg(target_os = "l4re")] { // L4Re is unix family but does not yet support unwinding. #[path = "dummy.rs"] - mod real_imp; - } else if #[cfg(all(target_env = "msvc", not(target_arch = "arm")))] { - // LLVM does not support unwinding on 32 bit ARM msvc (thumbv7a-pc-windows-msvc) - #[path = "seh.rs"] - mod real_imp; + mod imp; } else if #[cfg(any( all(target_family = "windows", target_env = "gnu"), target_os = "psp", @@ -58,7 +53,16 @@ cfg_if::cfg_if! { target_family = "wasm", ))] { #[path = "gcc.rs"] - mod real_imp; + mod imp; + } else if #[cfg(miri)] { + // Use the Miri runtime on Windows as miri doesn't support funclet based unwinding, + // only landingpad based unwinding. Also use the Miri runtime on unsupported platforms. + #[path = "miri.rs"] + mod imp; + } else if #[cfg(all(target_env = "msvc", not(target_arch = "arm")))] { + // LLVM does not support unwinding on 32 bit ARM msvc (thumbv7a-pc-windows-msvc) + #[path = "seh.rs"] + mod imp; } else { // Targets that don't support unwinding. // - os=none ("bare metal" targets) @@ -67,20 +71,7 @@ cfg_if::cfg_if! { // - nvptx64-nvidia-cuda // - arch=avr #[path = "dummy.rs"] - mod real_imp; - } -} - -cfg_if::cfg_if! { - if #[cfg(miri)] { - // Use the Miri runtime. - // We still need to also load the normal runtime above, as rustc expects certain lang - // items from there to be defined. - #[path = "miri.rs"] mod imp; - } else { - // Use the real runtime. - use real_imp as imp; } } diff --git a/library/panic_unwind/src/miri.rs b/library/panic_unwind/src/miri.rs index 4d21e846010e9..695adadd59b55 100644 --- a/library/panic_unwind/src/miri.rs +++ b/library/panic_unwind/src/miri.rs @@ -1,4 +1,5 @@ //! Unwinding panics for Miri. + use alloc::boxed::Box; use core::any::Any; diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index 04c3d3bf9c359..82c248c5a7ba1 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -157,7 +157,10 @@ mod imp { // going to be cross-lang LTOed anyway. However, using expose is shorter and // requires less unsafe. let addr: usize = ptr.expose_provenance(); + #[cfg(bootstrap)] let image_base = unsafe { addr_of!(__ImageBase) }.addr(); + #[cfg(not(bootstrap))] + let image_base = addr_of!(__ImageBase).addr(); let offset: usize = addr - image_base; Self(offset as u32) } @@ -250,7 +253,10 @@ extern "C" { // This is fine since the MSVC runtime uses string comparison on the type name // to match TypeDescriptors rather than pointer equality. static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor { + #[cfg(bootstrap)] pVFTable: unsafe { addr_of!(TYPE_INFO_VTABLE) } as *const _, + #[cfg(not(bootstrap))] + pVFTable: addr_of!(TYPE_INFO_VTABLE) as *const _, spare: core::ptr::null_mut(), name: TYPE_NAME, }; diff --git a/library/portable-simd/crates/core_simd/examples/dot_product.rs b/library/portable-simd/crates/core_simd/examples/dot_product.rs index f047010a65c16..75d152ae7f0e3 100644 --- a/library/portable-simd/crates/core_simd/examples/dot_product.rs +++ b/library/portable-simd/crates/core_simd/examples/dot_product.rs @@ -1,6 +1,5 @@ -// Code taken from the `packed_simd` crate -// Run this code with `cargo test --example dot_product` -//use std::iter::zip; +//! Code taken from the `packed_simd` crate. +//! Run this code with `cargo test --example dot_product`. #![feature(array_chunks)] #![feature(slice_as_chunks)] diff --git a/library/portable-simd/crates/core_simd/src/masks.rs b/library/portable-simd/crates/core_simd/src/masks.rs index e6e27c76a5e99..04de3a968276d 100644 --- a/library/portable-simd/crates/core_simd/src/masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks.rs @@ -137,7 +137,7 @@ where T: MaskElement, LaneCount: SupportedLaneCount, { - /// Construct a mask by setting all elements to the given value. + /// Constructs a mask by setting all elements to the given value. #[inline] pub fn splat(value: bool) -> Self { Self(mask_impl::Mask::splat(value)) @@ -288,7 +288,7 @@ where self.0.all() } - /// Create a bitmask from a mask. + /// Creates a bitmask from a mask. /// /// Each bit is set if the corresponding element in the mask is `true`. /// If the mask contains more than 64 elements, the bitmask is truncated to the first 64. @@ -298,7 +298,7 @@ where self.0.to_bitmask_integer() } - /// Create a mask from a bitmask. + /// Creates a mask from a bitmask. /// /// For each bit, if it is set, the corresponding element in the mask is set to `true`. /// If the mask contains more than 64 elements, the remainder are set to `false`. @@ -308,7 +308,7 @@ where Self(mask_impl::Mask::from_bitmask_integer(bitmask)) } - /// Create a bitmask vector from a mask. + /// Creates a bitmask vector from a mask. /// /// Each bit is set if the corresponding element in the mask is `true`. /// The remaining bits are unset. @@ -328,7 +328,7 @@ where self.0.to_bitmask_vector() } - /// Create a mask from a bitmask vector. + /// Creates a mask from a bitmask vector. /// /// For each bit, if it is set, the corresponding element in the mask is set to `true`. /// @@ -350,7 +350,7 @@ where Self(mask_impl::Mask::from_bitmask_vector(bitmask)) } - /// Find the index of the first set element. + /// Finds the index of the first set element. /// /// ``` /// # #![feature(portable_simd)] diff --git a/library/portable-simd/crates/core_simd/src/ops/assign.rs b/library/portable-simd/crates/core_simd/src/ops/assign.rs index 0e87785025a38..d21d867de26d6 100644 --- a/library/portable-simd/crates/core_simd/src/ops/assign.rs +++ b/library/portable-simd/crates/core_simd/src/ops/assign.rs @@ -1,4 +1,5 @@ //! Assignment operators + use super::*; use core::ops::{AddAssign, MulAssign}; // commutative binary op-assignment use core::ops::{BitAndAssign, BitOrAssign, BitXorAssign}; // commutative bit binary op-assignment diff --git a/library/portable-simd/crates/core_simd/src/ops/deref.rs b/library/portable-simd/crates/core_simd/src/ops/deref.rs index 89a60ba114146..0ff76cfba39bb 100644 --- a/library/portable-simd/crates/core_simd/src/ops/deref.rs +++ b/library/portable-simd/crates/core_simd/src/ops/deref.rs @@ -2,6 +2,7 @@ //! Ideally, Rust would take care of this itself, //! and method calls usually handle the LHS implicitly. //! But this is not the case with arithmetic ops. + use super::*; macro_rules! deref_lhs { diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs index 0f1719206c9ce..be635ea640b86 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -54,7 +54,7 @@ pub trait SimdConstPtr: Copy + Sealed { /// [`Self::with_exposed_provenance`] and returns the "address" portion. fn expose_provenance(self) -> Self::Usize; - /// Convert an address back to a pointer, picking up a previously "exposed" provenance. + /// Converts an address back to a pointer, picking up a previously "exposed" provenance. /// /// Equivalent to calling [`core::ptr::with_exposed_provenance`] on each element. fn with_exposed_provenance(addr: Self::Usize) -> Self; @@ -96,7 +96,7 @@ where fn cast(self) -> Self::CastPtr { // SimdElement currently requires zero-sized metadata, so this should never fail. // If this ever changes, `simd_cast_ptr` should produce a post-mono error. - use core::{mem::size_of, ptr::Pointee}; + use core::ptr::Pointee; assert_eq!(size_of::<::Metadata>(), 0); assert_eq!(size_of::<::Metadata>(), 0); diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs index 7ba996d149c0c..f6823a949e32a 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -51,7 +51,7 @@ pub trait SimdMutPtr: Copy + Sealed { /// [`Self::with_exposed_provenance`] and returns the "address" portion. fn expose_provenance(self) -> Self::Usize; - /// Convert an address back to a pointer, picking up a previously "exposed" provenance. + /// Converts an address back to a pointer, picking up a previously "exposed" provenance. /// /// Equivalent to calling [`core::ptr::with_exposed_provenance_mut`] on each element. fn with_exposed_provenance(addr: Self::Usize) -> Self; @@ -93,7 +93,7 @@ where fn cast(self) -> Self::CastPtr { // SimdElement currently requires zero-sized metadata, so this should never fail. // If this ever changes, `simd_cast_ptr` should produce a post-mono error. - use core::{mem::size_of, ptr::Pointee}; + use core::ptr::Pointee; assert_eq!(size_of::<::Metadata>(), 0); assert_eq!(size_of::<::Metadata>(), 0); diff --git a/library/portable-simd/crates/core_simd/src/swizzle.rs b/library/portable-simd/crates/core_simd/src/swizzle.rs index 71110bb282018..2f4f777b20e29 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle.rs @@ -69,12 +69,12 @@ pub macro simd_swizzle { } } -/// Create a vector from the elements of another vector. +/// Creates a vector from the elements of another vector. pub trait Swizzle { /// Map from the elements of the input vector to the output vector. const INDEX: [usize; N]; - /// Create a new vector from the elements of `vector`. + /// Creates a new vector from the elements of `vector`. /// /// Lane `i` of the output is `vector[Self::INDEX[i]]`. #[inline] @@ -109,7 +109,7 @@ pub trait Swizzle { } } - /// Create a new vector from the elements of `first` and `second`. + /// Creates a new vector from the elements of `first` and `second`. /// /// Lane `i` of the output is `concat[Self::INDEX[i]]`, where `concat` is the concatenation of /// `first` and `second`. @@ -145,7 +145,7 @@ pub trait Swizzle { } } - /// Create a new mask from the elements of `mask`. + /// Creates a new mask from the elements of `mask`. /// /// Element `i` of the output is `concat[Self::INDEX[i]]`, where `concat` is the concatenation of /// `first` and `second`. @@ -161,7 +161,7 @@ pub trait Swizzle { unsafe { Mask::from_int_unchecked(Self::swizzle(mask.to_int())) } } - /// Create a new mask from the elements of `first` and `second`. + /// Creates a new mask from the elements of `first` and `second`. /// /// Element `i` of the output is `concat[Self::INDEX[i]]`, where `concat` is the concatenation of /// `first` and `second`. diff --git a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs index 8a1079042f076..3b6388d0f2759 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs @@ -30,6 +30,8 @@ where use core::arch::arm::{uint8x8_t, vtbl1_u8}; #[cfg(target_arch = "wasm32")] use core::arch::wasm32 as wasm; + #[cfg(target_arch = "wasm64")] + use core::arch::wasm64 as wasm; #[cfg(target_arch = "x86")] use core::arch::x86; #[cfg(target_arch = "x86_64")] diff --git a/library/portable-simd/crates/core_simd/src/to_bytes.rs b/library/portable-simd/crates/core_simd/src/to_bytes.rs index 222526c4ab30a..4833ea9e11362 100644 --- a/library/portable-simd/crates/core_simd/src/to_bytes.rs +++ b/library/portable-simd/crates/core_simd/src/to_bytes.rs @@ -10,7 +10,7 @@ mod sealed { } use sealed::Sealed; -/// Convert SIMD vectors to vectors of bytes +/// Converts SIMD vectors to vectors of bytes pub trait ToBytes: Sealed { /// This type, reinterpreted as bytes. type Bytes: Copy @@ -22,26 +22,26 @@ pub trait ToBytes: Sealed { + SimdUint + 'static; - /// Return the memory representation of this integer as a byte array in native byte + /// Returns the memory representation of this integer as a byte array in native byte /// order. fn to_ne_bytes(self) -> Self::Bytes; - /// Return the memory representation of this integer as a byte array in big-endian + /// Returns the memory representation of this integer as a byte array in big-endian /// (network) byte order. fn to_be_bytes(self) -> Self::Bytes; - /// Return the memory representation of this integer as a byte array in little-endian + /// Returns the memory representation of this integer as a byte array in little-endian /// byte order. fn to_le_bytes(self) -> Self::Bytes; - /// Create a native endian integer value from its memory representation as a byte array + /// Creates a native endian integer value from its memory representation as a byte array /// in native endianness. fn from_ne_bytes(bytes: Self::Bytes) -> Self; - /// Create an integer value from its representation as a byte array in big endian. + /// Creates an integer value from its representation as a byte array in big endian. fn from_be_bytes(bytes: Self::Bytes) -> Self; - /// Create an integer value from its representation as a byte array in little endian. + /// Creates an integer value from its representation as a byte array in little endian. fn from_le_bytes(bytes: Self::Bytes) -> Self; } diff --git a/library/portable-simd/crates/core_simd/src/vector.rs b/library/portable-simd/crates/core_simd/src/vector.rs index 8dbdfc0e1fe03..3e23916914963 100644 --- a/library/portable-simd/crates/core_simd/src/vector.rs +++ b/library/portable-simd/crates/core_simd/src/vector.rs @@ -187,7 +187,7 @@ where unsafe { &mut *(self as *mut Self as *mut [T; N]) } } - /// Load a vector from an array of `T`. + /// Loads a vector from an array of `T`. /// /// This function is necessary since `repr(simd)` has padding for non-power-of-2 vectors (at the time of writing). /// With padding, `read_unaligned` will read past the end of an array of N elements. @@ -567,7 +567,7 @@ where unsafe { Self::gather_select_ptr(ptrs, enable, or) } } - /// Read elementwise from pointers into a SIMD vector. + /// Reads elementwise from pointers into a SIMD vector. /// /// # Safety /// @@ -808,7 +808,7 @@ where } } - /// Write pointers elementwise into a SIMD vector. + /// Writes pointers elementwise into a SIMD vector. /// /// # Safety /// diff --git a/library/proc_macro/src/bridge/arena.rs b/library/proc_macro/src/bridge/arena.rs index f81f2152cd046..1d5986093c8a4 100644 --- a/library/proc_macro/src/bridge/arena.rs +++ b/library/proc_macro/src/bridge/arena.rs @@ -5,12 +5,9 @@ //! being built at the same time as `std`. use std::cell::{Cell, RefCell}; -use std::cmp; use std::mem::MaybeUninit; use std::ops::Range; -use std::ptr; -use std::slice; -use std::str; +use std::{cmp, ptr, slice, str}; // The arenas start with PAGE-sized chunks, and then each new chunk is twice as // big as its predecessor, up until we reach HUGE_PAGE-sized chunks, whereupon diff --git a/library/proc_macro/src/bridge/buffer.rs b/library/proc_macro/src/bridge/buffer.rs index 48030f8d82dca..78fcd1999b2f3 100644 --- a/library/proc_macro/src/bridge/buffer.rs +++ b/library/proc_macro/src/bridge/buffer.rs @@ -1,7 +1,7 @@ //! Buffer management for same-process client<->server communication. use std::io::{self, Write}; -use std::mem; +use std::mem::{self, ManuallyDrop}; use std::ops::{Deref, DerefMut}; use std::slice; @@ -119,7 +119,9 @@ impl Write for Buffer { } impl Drop for Buffer { - #[inline] + // HACK(nbdd0121): Hack to prevent LLVM < 17.0.4 from misoptimising, + // change to `#[inline]` if fixed. + #[inline(never)] fn drop(&mut self) { let b = self.take(); (b.drop)(b); @@ -127,17 +129,16 @@ impl Drop for Buffer { } impl From> for Buffer { - fn from(mut v: Vec) -> Self { + fn from(v: Vec) -> Self { + let mut v = ManuallyDrop::new(v); let (data, len, capacity) = (v.as_mut_ptr(), v.len(), v.capacity()); - mem::forget(v); // This utility function is nested in here because it can *only* // be safely called on `Buffer`s created by *this* `proc_macro`. fn to_vec(b: Buffer) -> Vec { unsafe { - let Buffer { data, len, capacity, .. } = b; - mem::forget(b); - Vec::from_raw_parts(data, len, capacity) + let b = ManuallyDrop::new(b); + Vec::from_raw_parts(b.data, b.len, b.capacity) } } diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index faca745e56f74..5a1086527a127 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -1,11 +1,11 @@ //! Client-side types. -use super::*; - use std::cell::RefCell; use std::marker::PhantomData; use std::sync::atomic::AtomicU32; +use super::*; + macro_rules! define_client_handles { ( 'owned: $($oty:ident,)* @@ -51,9 +51,7 @@ macro_rules! define_client_handles { impl Encode for $oty { fn encode(self, w: &mut Writer, s: &mut S) { - let handle = self.handle; - mem::forget(self); - handle.encode(w, s); + mem::ManuallyDrop::new(self).handle.encode(w, s); } } @@ -192,10 +190,11 @@ impl<'a> !Sync for Bridge<'a> {} #[allow(unsafe_code)] mod state { - use super::Bridge; use std::cell::{Cell, RefCell}; use std::ptr; + use super::Bridge; + thread_local! { static BRIDGE_STATE: Cell<*const ()> = const { Cell::new(ptr::null()) }; } diff --git a/library/proc_macro/src/bridge/fxhash.rs b/library/proc_macro/src/bridge/fxhash.rs index f4e9054419721..74a41451825ff 100644 --- a/library/proc_macro/src/bridge/fxhash.rs +++ b/library/proc_macro/src/bridge/fxhash.rs @@ -5,9 +5,7 @@ //! on the `rustc_hash` crate. use std::collections::HashMap; -use std::hash::BuildHasherDefault; -use std::hash::Hasher; -use std::mem::size_of; +use std::hash::{BuildHasherDefault, Hasher}; use std::ops::BitXor; /// Type alias for a hashmap using the `fx` hash algorithm. @@ -69,7 +67,7 @@ impl Hasher for FxHasher { hash.add_to_hash(u16::from_ne_bytes(bytes[..2].try_into().unwrap()) as usize); bytes = &bytes[2..]; } - if (size_of::() > 1) && bytes.len() >= 1 { + if (size_of::() > 1) && !bytes.is_empty() { hash.add_to_hash(bytes[0] as usize); } self.hash = hash.hash; diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 8553e8d5e4fd6..03c3e697cfe2b 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -8,16 +8,12 @@ #![deny(unsafe_code)] -use crate::{Delimiter, Level, Spacing}; -use std::fmt; use std::hash::Hash; -use std::marker; -use std::mem; -use std::ops::Bound; -use std::ops::Range; -use std::panic; +use std::ops::{Bound, Range}; use std::sync::Once; -use std::thread; +use std::{fmt, marker, mem, panic, thread}; + +use crate::{Delimiter, Level, Spacing}; /// Higher-order macro describing the server RPC API, allowing automatic /// generation of type-safe Rust APIs, both client-side and server-side. diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 6d75a5a627c82..202a8e04543b2 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -264,9 +264,9 @@ impl From> for PanicMessage { } } -impl Into> for PanicMessage { - fn into(self) -> Box { - match self { +impl From for Box { + fn from(val: PanicMessage) -> Self { + match val { PanicMessage::StaticStr(s) => Box::new(s), PanicMessage::String(s) => Box::new(s), PanicMessage::Unknown => { diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 0dbd4bac85c9f..692b6038a3872 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -1,10 +1,10 @@ //! Server-side traits. -use super::*; - use std::cell::Cell; use std::marker::PhantomData; +use super::*; + macro_rules! define_server_handles { ( 'owned: $($oty:ident,)* @@ -350,7 +350,7 @@ where /// A message pipe used for communicating between server and client threads. pub trait MessagePipe: Sized { - /// Create a new pair of endpoints for the message pipe. + /// Creates a new pair of endpoints for the message pipe. fn new() -> (Self, Self); /// Send a message to the other endpoint of this pipe. diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs index 86ce2cc189588..37aaee6b21553 100644 --- a/library/proc_macro/src/bridge/symbol.rs +++ b/library/proc_macro/src/bridge/symbol.rs @@ -28,7 +28,7 @@ impl Symbol { INTERNER.with_borrow_mut(|i| i.intern(string)) } - /// Create a new `Symbol` for an identifier. + /// Creates a new `Symbol` for an identifier. /// /// Validates and normalizes before converting it to a symbol. pub(crate) fn new_ident(string: &str, is_raw: bool) -> Self { @@ -63,7 +63,7 @@ impl Symbol { INTERNER.with_borrow_mut(|i| i.clear()); } - /// Check if the ident is a valid ASCII identifier. + /// Checks if the ident is a valid ASCII identifier. /// /// This is a short-circuit which is cheap to implement within the /// proc-macro client to avoid RPC when creating simple idents, but may @@ -177,7 +177,7 @@ impl Interner { name } - /// Read a symbol's value from the store while it is held. + /// Reads a symbol's value from the store while it is held. fn get(&self, symbol: Symbol) -> &str { // NOTE: Subtract out the offset which was added to make the symbol // nonzero and prevent symbol name re-use. diff --git a/library/proc_macro/src/escape.rs b/library/proc_macro/src/escape.rs new file mode 100644 index 0000000000000..87a4d1d50fd48 --- /dev/null +++ b/library/proc_macro/src/escape.rs @@ -0,0 +1,57 @@ +#[derive(Copy, Clone)] +pub(crate) struct EscapeOptions { + /// Produce \'. + pub escape_single_quote: bool, + /// Produce \". + pub escape_double_quote: bool, + /// Produce \x escapes for non-ASCII, and use \x rather than \u for ASCII + /// control characters. + pub escape_nonascii: bool, +} + +pub(crate) fn escape_bytes(bytes: &[u8], opt: EscapeOptions) -> String { + let mut repr = String::new(); + + if opt.escape_nonascii { + for &byte in bytes { + escape_single_byte(byte, opt, &mut repr); + } + } else { + let mut chunks = bytes.utf8_chunks(); + while let Some(chunk) = chunks.next() { + for ch in chunk.valid().chars() { + escape_single_char(ch, opt, &mut repr); + } + for &byte in chunk.invalid() { + escape_single_byte(byte, opt, &mut repr); + } + } + } + + repr +} + +fn escape_single_byte(byte: u8, opt: EscapeOptions, repr: &mut String) { + if byte == b'\0' { + repr.push_str("\\0"); + } else if (byte == b'\'' && !opt.escape_single_quote) + || (byte == b'"' && !opt.escape_double_quote) + { + repr.push(byte as char); + } else { + // Escapes \t, \r, \n, \\, \', \", and uses \x## for non-ASCII and + // for ASCII control characters. + repr.extend(byte.escape_ascii().map(char::from)); + } +} + +fn escape_single_char(ch: char, opt: EscapeOptions, repr: &mut String) { + if (ch == '\'' && !opt.escape_single_quote) || (ch == '"' && !opt.escape_double_quote) { + repr.push(ch); + } else { + // Escapes \0, \t, \r, \n, \\, \', \", and uses \u{...} for + // non-printable characters and for Grapheme_Extend characters, which + // includes things like U+0300 "Combining Grave Accent". + repr.extend(ch.escape_debug()); + } +} diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 3d7d36b27e53b..c271ac1870624 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -37,15 +37,14 @@ #![recursion_limit = "256"] #![allow(internal_features)] #![deny(ffi_unwind_calls)] +#![warn(rustdoc::unescaped_backticks)] #[unstable(feature = "proc_macro_internals", issue = "27812")] #[doc(hidden)] pub mod bridge; mod diagnostic; - -#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] -pub use diagnostic::{Diagnostic, Level, MultiSpan}; +mod escape; use std::ffi::CStr; use std::ops::{Range, RangeBounds}; @@ -53,6 +52,11 @@ use std::path::PathBuf; use std::str::FromStr; use std::{error, fmt}; +#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] +pub use diagnostic::{Diagnostic, Level, MultiSpan}; + +use crate::escape::{escape_bytes, EscapeOptions}; + /// Determines whether proc_macro has been made accessible to the currently /// running program. /// @@ -1356,40 +1360,61 @@ impl Literal { /// String literal. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn string(string: &str) -> Literal { - let quoted = format!("{:?}", string); - assert!(quoted.starts_with('"') && quoted.ends_with('"')); - let symbol = "ed[1..quoted.len() - 1]; - Literal::new(bridge::LitKind::Str, symbol, None) + let escape = EscapeOptions { + escape_single_quote: false, + escape_double_quote: true, + escape_nonascii: false, + }; + let repr = escape_bytes(string.as_bytes(), escape); + Literal::new(bridge::LitKind::Str, &repr, None) } /// Character literal. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn character(ch: char) -> Literal { - let quoted = format!("{:?}", ch); - assert!(quoted.starts_with('\'') && quoted.ends_with('\'')); - let symbol = "ed[1..quoted.len() - 1]; - Literal::new(bridge::LitKind::Char, symbol, None) + let escape = EscapeOptions { + escape_single_quote: true, + escape_double_quote: false, + escape_nonascii: false, + }; + let repr = escape_bytes(ch.encode_utf8(&mut [0u8; 4]).as_bytes(), escape); + Literal::new(bridge::LitKind::Char, &repr, None) } /// Byte character literal. #[stable(feature = "proc_macro_byte_character", since = "1.79.0")] pub fn byte_character(byte: u8) -> Literal { - let string = [byte].escape_ascii().to_string(); - Literal::new(bridge::LitKind::Byte, &string, None) + let escape = EscapeOptions { + escape_single_quote: true, + escape_double_quote: false, + escape_nonascii: true, + }; + let repr = escape_bytes(&[byte], escape); + Literal::new(bridge::LitKind::Byte, &repr, None) } /// Byte string literal. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn byte_string(bytes: &[u8]) -> Literal { - let string = bytes.escape_ascii().to_string(); - Literal::new(bridge::LitKind::ByteStr, &string, None) + let escape = EscapeOptions { + escape_single_quote: false, + escape_double_quote: true, + escape_nonascii: true, + }; + let repr = escape_bytes(bytes, escape); + Literal::new(bridge::LitKind::ByteStr, &repr, None) } /// C string literal. #[stable(feature = "proc_macro_c_str_literals", since = "1.79.0")] pub fn c_string(string: &CStr) -> Literal { - let string = string.to_bytes().escape_ascii().to_string(); - Literal::new(bridge::LitKind::CStr, &string, None) + let escape = EscapeOptions { + escape_single_quote: false, + escape_double_quote: true, + escape_nonascii: false, + }; + let repr = escape_bytes(string.to_bytes(), escape); + Literal::new(bridge::LitKind::CStr, &repr, None) } /// Returns the span encompassing this literal. @@ -1521,10 +1546,10 @@ impl fmt::Debug for Literal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Literal") // format the kind on one line even in {:#?} mode - .field("kind", &format_args!("{:?}", &self.0.kind)) + .field("kind", &format_args!("{:?}", self.0.kind)) .field("symbol", &self.0.symbol) // format `Some("...")` on one line even in {:#?} mode - .field("suffix", &format_args!("{:?}", &self.0.suffix)) + .field("suffix", &format_args!("{:?}", self.0.suffix)) .field("span", &self.0.span) .finish() } diff --git a/library/rtstartup/rsbegin.rs b/library/rtstartup/rsbegin.rs index 14bce2bbeee2b..9a3d95bd8ddfb 100644 --- a/library/rtstartup/rsbegin.rs +++ b/library/rtstartup/rsbegin.rs @@ -29,6 +29,8 @@ trait Copy {} #[lang = "freeze"] auto trait Freeze {} +impl Copy for *mut T {} + #[lang = "drop_in_place"] #[inline] #[allow(unconditional_recursion)] diff --git a/library/rtstartup/rsend.rs b/library/rtstartup/rsend.rs index 714643c83866f..2514eb0034402 100644 --- a/library/rtstartup/rsend.rs +++ b/library/rtstartup/rsend.rs @@ -17,6 +17,8 @@ trait Copy {} #[lang = "freeze"] auto trait Freeze {} +impl Copy for *mut T {} + #[lang = "drop_in_place"] #[inline] #[allow(unconditional_recursion)] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index e56f03808b311..fe601855cc1e7 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,27 +17,45 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "0.1.105" } +compiler_builtins = { version = "0.1.114" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } -hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep-of-std'] } -std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] } +hashbrown = { version = "0.14", default-features = false, features = [ + 'rustc-dep-of-std', +] } +std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = [ + 'rustc-dep-of-std', +] } # Dependencies of the `backtrace` crate -rustc-demangle = { version = "0.1.21", features = ['rustc-dep-of-std'] } +rustc-demangle = { version = "0.1.24", features = ['rustc-dep-of-std'] } [target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies] miniz_oxide = { version = "0.7.0", optional = true, default-features = false } -addr2line = { version = "0.21.0", optional = true, default-features = false } +addr2line = { version = "0.22.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true } +libc = { version = "0.2.153", default-features = false, features = [ + 'rustc-dep-of-std', +], public = true } [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies] -object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } +object = { version = "0.36.0", default-features = false, optional = true, features = [ + 'read_core', + 'elf', + 'macho', + 'pe', + 'unaligned', + 'archive', +] } [target.'cfg(target_os = "aix")'.dependencies] -object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'xcoff', 'unaligned', 'archive'] } +object = { version = "0.36.0", default-features = false, optional = true, features = [ + 'read_core', + 'xcoff', + 'unaligned', + 'archive', +] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } @@ -47,23 +65,29 @@ rand_xorshift = "0.3.0" dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] } [target.x86_64-fortanix-unknown-sgx.dependencies] -fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public = true } +fortanix-sgx-abi = { version = "0.5.0", features = [ + 'rustc-dep-of-std', +], public = true } [target.'cfg(target_os = "hermit")'.dependencies] -hermit-abi = { version = "0.3.9", features = ['rustc-dep-of-std'], public = true } +hermit-abi = { version = "0.4.0", features = [ + 'rustc-dep-of-std', +], public = true } [target.'cfg(target_os = "wasi")'.dependencies] -wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } +wasi = { version = "0.11.0", features = [ + 'rustc-dep-of-std', +], default-features = false } [target.'cfg(target_os = "uefi")'.dependencies] -r-efi = { version = "4.2.0", features = ['rustc-dep-of-std'] } +r-efi = { version = "4.5.0", features = ['rustc-dep-of-std'] } r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std'] } [features] backtrace = [ - 'addr2line/rustc-dep-of-std', - 'object/rustc-dep-of-std', - 'miniz_oxide/rustc-dep-of-std', + 'addr2line/rustc-dep-of-std', + 'object/rustc-dep-of-std', + 'miniz_oxide/rustc-dep-of-std', ] panic-unwind = ["panic_unwind"] @@ -71,13 +95,16 @@ profiler = ["profiler_builtins"] compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-mem = ["alloc/compiler-builtins-mem"] compiler-builtins-no-asm = ["alloc/compiler-builtins-no-asm"] +compiler-builtins-no-f16-f128 = ["alloc/compiler-builtins-no-f16-f128"] compiler-builtins-mangled-names = ["alloc/compiler-builtins-mangled-names"] -compiler-builtins-weak-intrinsics = ["alloc/compiler-builtins-weak-intrinsics"] llvm-libunwind = ["unwind/llvm-libunwind"] system-llvm-libunwind = ["unwind/system-llvm-libunwind"] # Make panics and failed asserts immediately abort without formatting any message -panic_immediate_abort = ["core/panic_immediate_abort", "alloc/panic_immediate_abort"] +panic_immediate_abort = [ + "core/panic_immediate_abort", + "alloc/panic_immediate_abort", +] # Choose algorithms that are optimized for binary size instead of runtime performance optimize_for_size = ["core/optimize_for_size", "alloc/optimize_for_size"] @@ -87,12 +114,21 @@ std_detect_file_io = ["std_detect/std_detect_file_io"] std_detect_dlsym_getauxval = ["std_detect/std_detect_dlsym_getauxval"] std_detect_env_override = ["std_detect/std_detect_env_override"] +# Enable using raw-dylib for Windows imports. +# This will eventually be the default. +windows_raw_dylib = [] + [package.metadata.fortanix-sgx] # Maximum possible number of threads when testing threads = 125 # Maximum heap size heap_size = 0x8000000 +[[test]] +name = "pipe-subprocess" +path = "tests/pipe_subprocess.rs" +harness = false + [[bench]] name = "stdbenches" path = "benches/lib.rs" @@ -100,9 +136,6 @@ test = true [lints.rust.unexpected_cfgs] level = "warn" -# x.py uses beta cargo, so `check-cfg` entries do not yet take effect -# for rust-lang/rust. But for users of `-Zbuild-std` it does. -# The unused warning is waiting for rust-lang/cargo#13925 to reach beta. check-cfg = [ 'cfg(bootstrap)', 'cfg(target_arch, values("xtensa"))', diff --git a/library/std/benches/hash/map.rs b/library/std/benches/hash/map.rs index bf646cbae47db..d6023c8212b89 100644 --- a/library/std/benches/hash/map.rs +++ b/library/std/benches/hash/map.rs @@ -1,6 +1,7 @@ #![cfg(test)] use std::collections::HashMap; + use test::Bencher; #[bench] diff --git a/library/std/benches/hash/set_ops.rs b/library/std/benches/hash/set_ops.rs index 1a4c4a66ee9e0..b97e3b450850e 100644 --- a/library/std/benches/hash/set_ops.rs +++ b/library/std/benches/hash/set_ops.rs @@ -1,4 +1,5 @@ use std::collections::HashSet; + use test::Bencher; #[bench] diff --git a/library/std/build.rs b/library/std/build.rs index 7d975df545ecf..9b58dd53ba20a 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -7,6 +7,10 @@ fn main() { let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").expect("CARGO_CFG_TARGET_VENDOR was not set"); let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("CARGO_CFG_TARGET_ENV was not set"); + let target_pointer_width: u32 = env::var("CARGO_CFG_TARGET_POINTER_WIDTH") + .expect("CARGO_CFG_TARGET_POINTER_WIDTH was not set") + .parse() + .unwrap(); println!("cargo:rustc-check-cfg=cfg(netbsd10)"); if target_os == "netbsd" && env::var("RUSTC_STD_NETBSD10").is_ok() { @@ -70,4 +74,66 @@ fn main() { println!("cargo:rustc-cfg=backtrace_in_libstd"); println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap()); + + // Emit these on platforms that have no known ABI bugs, LLVM selection bugs, lowering bugs, + // missing symbols, or other problems, to determine when tests get run. + // If more broken platforms are found, please update the tracking issue at + // + // + // Some of these match arms are redundant; the goal is to separate reasons that the type is + // unreliable, even when multiple reasons might fail the same platform. + println!("cargo:rustc-check-cfg=cfg(reliable_f16)"); + println!("cargo:rustc-check-cfg=cfg(reliable_f128)"); + + let has_reliable_f16 = match (target_arch.as_str(), target_os.as_str()) { + // Selection failure until recent LLVM + // FIXME(llvm19): can probably be removed at the version bump + ("loongarch64", _) => false, + // Selection failure + ("s390x", _) => false, + // Unsupported + ("arm64ec", _) => false, + // MinGW ABI bugs + ("x86_64", "windows") => false, + // x86 has ABI bugs that show up with optimizations. This should be partially fixed with + // the compiler-builtins update. + ("x86" | "x86_64", _) => false, + // Missing `__gnu_h2f_ieee` and `__gnu_f2h_ieee` + ("powerpc" | "powerpc64", _) => false, + // Missing `__gnu_h2f_ieee` and `__gnu_f2h_ieee` + ("mips" | "mips32r6" | "mips64" | "mips64r6", _) => false, + // Missing `__extendhfsf` and `__truncsfhf` + ("riscv32" | "riscv64", _) => false, + // Most OSs are missing `__extendhfsf` and `__truncsfhf` + (_, "linux" | "macos") => true, + // Almost all OSs besides Linux and MacOS are missing symbols until compiler-builtins can + // be updated. will get some of these, the + // next CB update should get the rest. + _ => false, + }; + + let has_reliable_f128 = match (target_arch.as_str(), target_os.as_str()) { + // Unsupported + ("arm64ec", _) => false, + // ABI and precision bugs + // + ("powerpc" | "powerpc64", _) => false, + // Selection bug + ("nvptx64", _) => false, + // ABI unsupported + ("sparc", _) => false, + // MinGW ABI bugs + ("x86_64", "windows") => false, + // 64-bit Linux is about the only platform to have f128 symbols by default + (_, "linux") if target_pointer_width == 64 => true, + // Same as for f16, except MacOS is also missing f128 symbols. + _ => false, + }; + + if has_reliable_f16 { + println!("cargo:rustc-cfg=reliable_f16"); + } + if has_reliable_f128 { + println!("cargo:rustc-cfg=reliable_f128"); + } } diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index b98fbbf762fa2..5d51d6a0c78a8 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -56,10 +56,9 @@ #![deny(unsafe_op_in_unsafe_fn)] #![stable(feature = "alloc_module", since = "1.28.0")] -use core::hint; use core::ptr::NonNull; use core::sync::atomic::{AtomicPtr, Ordering}; -use core::{mem, ptr}; +use core::{hint, mem, ptr}; #[stable(feature = "alloc_module", since = "1.28.0")] #[doc(inline)] @@ -73,7 +72,9 @@ pub use alloc_crate::alloc::*; /// work, such as to serve alignment requests greater than the alignment /// provided directly by the backing system allocator. /// -/// This type implements the `GlobalAlloc` trait and Rust programs by default +/// This type implements the [`GlobalAlloc`] trait. Currently the default +/// global allocator is unspecified. Libraries, however, like `cdylib`s and +/// `staticlib`s are guaranteed to use the [`System`] by default and as such /// work as if they had this definition: /// /// ```rust diff --git a/library/std/src/ascii.rs b/library/std/src/ascii.rs index b18ab50de123e..3a2880fd50904 100644 --- a/library/std/src/ascii.rs +++ b/library/std/src/ascii.rs @@ -13,11 +13,10 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::ascii::{escape_default, EscapeDefault}; - #[unstable(feature = "ascii_char", issue = "110998")] pub use core::ascii::Char; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::ascii::{escape_default, EscapeDefault}; /// Extension methods for ASCII-subset only operations. /// diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index 475b3e7eb9312..7df9a8a14b00c 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -89,13 +89,13 @@ mod tests; // a backtrace or actually symbolizing it. use crate::backtrace_rs::{self, BytesOrWideString}; -use crate::env; use crate::ffi::c_void; -use crate::fmt; use crate::panic::UnwindSafe; -use crate::sync::atomic::{AtomicU8, Ordering::Relaxed}; +use crate::sync::atomic::AtomicU8; +use crate::sync::atomic::Ordering::Relaxed; use crate::sync::LazyLock; -use crate::sys_common::backtrace::{lock, output_filename, set_image_base}; +use crate::sys::backtrace::{lock, output_filename, set_image_base}; +use crate::{env, fmt}; /// A captured OS thread stack backtrace. /// @@ -271,7 +271,7 @@ impl Backtrace { enabled } - /// Capture a stack backtrace of the current thread. + /// Captures a stack backtrace of the current thread. /// /// This function will capture a stack backtrace of the current OS thread of /// execution, returning a `Backtrace` type which can be later used to print @@ -428,39 +428,43 @@ impl fmt::Display for Backtrace { } } -type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync + UnwindSafe; - -fn lazy_resolve(mut capture: Capture) -> LazyResolve { - move || { - // Use the global backtrace lock to synchronize this as it's a - // requirement of the `backtrace` crate, and then actually resolve - // everything. - let _lock = lock(); - for frame in capture.frames.iter_mut() { - let symbols = &mut frame.symbols; - let frame = match &frame.frame { - RawFrame::Actual(frame) => frame, - #[cfg(test)] - RawFrame::Fake => unimplemented!(), - }; - unsafe { - backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { - symbols.push(BacktraceSymbol { - name: symbol.name().map(|m| m.as_bytes().to_vec()), - filename: symbol.filename_raw().map(|b| match b { - BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()), - BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()), - }), - lineno: symbol.lineno(), - colno: symbol.colno(), +mod helper { + use super::*; + pub(super) type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync + UnwindSafe; + + pub(super) fn lazy_resolve(mut capture: Capture) -> LazyResolve { + move || { + // Use the global backtrace lock to synchronize this as it's a + // requirement of the `backtrace` crate, and then actually resolve + // everything. + let _lock = lock(); + for frame in capture.frames.iter_mut() { + let symbols = &mut frame.symbols; + let frame = match &frame.frame { + RawFrame::Actual(frame) => frame, + #[cfg(test)] + RawFrame::Fake => unimplemented!(), + }; + unsafe { + backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { + symbols.push(BacktraceSymbol { + name: symbol.name().map(|m| m.as_bytes().to_vec()), + filename: symbol.filename_raw().map(|b| match b { + BytesOrWideString::Bytes(b) => BytesOrWide::Bytes(b.to_owned()), + BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()), + }), + lineno: symbol.lineno(), + colno: symbol.colno(), + }); }); - }); + } } - } - capture + capture + } } } +use helper::*; impl RawFrame { fn ip(&self) -> *mut c_void { diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 5039f0b6bb289..822fa5791e300 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1,13 +1,11 @@ #[cfg(test)] mod tests; -use self::Entry::*; - use hashbrown::hash_map as base; +use self::Entry::*; use crate::borrow::Borrow; -use crate::collections::TryReserveError; -use crate::collections::TryReserveErrorKind; +use crate::collections::{TryReserveError, TryReserveErrorKind}; use crate::error::Error; use crate::fmt::{self, Debug}; use crate::hash::{BuildHasher, Hash, RandomState}; @@ -1018,7 +1016,7 @@ where K: Borrow, Q: Hash + Eq, { - self.base.get_many_unchecked_mut(ks) + unsafe { self.base.get_many_unchecked_mut(ks) } } /// Returns `true` if the map contains a value for the specified key. @@ -1218,7 +1216,7 @@ where /// will cause the map to produce seemingly random results. Higher-level and /// more foolproof APIs like `entry` should be preferred when possible. /// - /// In particular, the hash used to initialized the raw entry must still be + /// In particular, the hash used to initialize the raw entry must still be /// consistent with the hash of the key that is ultimately stored in the entry. /// This is because implementations of HashMap may need to recompute hashes /// when resizing, at which point only the keys are available. diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index 8585376abc18b..6641197c3724a 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -1,11 +1,12 @@ +use rand::Rng; +use realstd::collections::TryReserveErrorKind::*; + use super::Entry::{Occupied, Vacant}; use super::HashMap; use crate::assert_matches::assert_matches; use crate::cell::RefCell; use crate::hash::RandomState; use crate::test_helpers::test_rng; -use rand::Rng; -use realstd::collections::TryReserveErrorKind::*; // https://github.com/rust-lang/rust/issues/62301 fn _assert_hashmap_is_unwind_safe() { @@ -946,7 +947,6 @@ fn test_raw_entry() { mod test_extract_if { use super::*; - use crate::panic::{catch_unwind, AssertUnwindSafe}; use crate::sync::atomic::{AtomicUsize, Ordering}; diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index f0a498fc7bbca..d611353b0d3f2 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -3,6 +3,7 @@ mod tests; use hashbrown::hash_set as base; +use super::map::map_try_reserve_error; use crate::borrow::Borrow; use crate::collections::TryReserveError; use crate::fmt; @@ -10,8 +11,6 @@ use crate::hash::{BuildHasher, Hash, RandomState}; use crate::iter::{Chain, FusedIterator}; use crate::ops::{BitAnd, BitOr, BitXor, Sub}; -use super::map::map_try_reserve_error; - /// A [hash set] implemented as a `HashMap` where the value is `()`. /// /// As with the [`HashMap`] type, a `HashSet` requires that the elements diff --git a/library/std/src/collections/hash/set/tests.rs b/library/std/src/collections/hash/set/tests.rs index a188409004305..4e6351652721f 100644 --- a/library/std/src/collections/hash/set/tests.rs +++ b/library/std/src/collections/hash/set/tests.rs @@ -1,5 +1,4 @@ use super::HashSet; - use crate::hash::RandomState; use crate::panic::{catch_unwind, AssertUnwindSafe}; use crate::sync::atomic::{AtomicU32, Ordering}; diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs index 1389d24a8c519..3b04412e76630 100644 --- a/library/std/src/collections/mod.rs +++ b/library/std/src/collections/mod.rs @@ -401,12 +401,14 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[stable(feature = "rust1", since = "1.0.0")] -// FIXME(#82080) The deprecation here is only theoretical, and does not actually produce a warning. -#[deprecated(note = "moved to `std::ops::Bound`", since = "1.26.0")] -#[doc(hidden)] -pub use crate::ops::Bound; - +#[stable(feature = "try_reserve", since = "1.57.0")] +pub use alloc_crate::collections::TryReserveError; +#[unstable( + feature = "try_reserve_kind", + reason = "Uncertain how much info should be exposed", + issue = "48043" +)] +pub use alloc_crate::collections::TryReserveErrorKind; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::collections::{binary_heap, btree_map, btree_set}; #[stable(feature = "rust1", since = "1.0.0")] @@ -422,15 +424,11 @@ pub use self::hash_map::HashMap; #[stable(feature = "rust1", since = "1.0.0")] #[doc(inline)] pub use self::hash_set::HashSet; - -#[stable(feature = "try_reserve", since = "1.57.0")] -pub use alloc_crate::collections::TryReserveError; -#[unstable( - feature = "try_reserve_kind", - reason = "Uncertain how much info should be exposed", - issue = "48043" -)] -pub use alloc_crate::collections::TryReserveErrorKind; +#[stable(feature = "rust1", since = "1.0.0")] +// FIXME(#82080) The deprecation here is only theoretical, and does not actually produce a warning. +#[deprecated(note = "moved to `std::ops::Bound`", since = "1.26.0")] +#[doc(hidden)] +pub use crate::ops::Bound; mod hash; @@ -439,7 +437,6 @@ pub mod hash_map { //! A hash map implemented with quadratic probing and SIMD lookup. #[stable(feature = "rust1", since = "1.0.0")] pub use super::hash::map::*; - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub use crate::hash::random::DefaultHasher; #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 6f8ac17f12c70..50ae83090c7e1 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -15,11 +15,9 @@ mod tests; use crate::error::Error; use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::io; use crate::path::{Path, PathBuf}; -use crate::sys; use crate::sys::os as os_imp; +use crate::{fmt, io, sys}; /// Returns the current working directory as a [`PathBuf`]. /// @@ -120,11 +118,8 @@ pub struct VarsOs { /// # Examples /// /// ``` -/// use std::env; -/// -/// // We will iterate through the references to the element returned by -/// // env::vars(); -/// for (key, value) in env::vars() { +/// // Print all environment variables. +/// for (key, value) in std::env::vars() { /// println!("{key}: {value}"); /// } /// ``` @@ -150,11 +145,8 @@ pub fn vars() -> Vars { /// # Examples /// /// ``` -/// use std::env; -/// -/// // We will iterate through the references to the element returned by -/// // env::vars_os(); -/// for (key, value) in env::vars_os() { +/// // Print all environment variables. +/// for (key, value) in std::env::vars_os() { /// println!("{key:?}: {value:?}"); /// } /// ``` @@ -318,22 +310,27 @@ impl Error for VarError { /// /// # Safety /// -/// Even though this function is currently not marked as `unsafe`, it needs to -/// be because invoking it can cause undefined behaviour. The function will be -/// marked `unsafe` in a future version of Rust. This is tracked in -/// [rust#27970](https://github.com/rust-lang/rust/issues/27970). -/// /// This function is safe to call in a single-threaded program. /// -/// In multi-threaded programs, you must ensure that are no other threads -/// concurrently writing or *reading*(!) from the environment through functions -/// other than the ones in this module. You are responsible for figuring out -/// how to achieve this, but we strongly suggest not using `set_var` or -/// `remove_var` in multi-threaded programs at all. -/// -/// Most C libraries, including libc itself do not advertise which functions -/// read from the environment. Even functions from the Rust standard library do -/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`]. +/// This function is also always safe to call on Windows, in single-threaded +/// and multi-threaded programs. +/// +/// In multi-threaded programs on other operating systems, the only safe option is +/// to not use `set_var` or `remove_var` at all. +/// +/// The exact requirement is: you +/// must ensure that there are no other threads concurrently writing or +/// *reading*(!) the environment through functions or global variables other +/// than the ones in this module. The problem is that these operating systems +/// do not provide a thread-safe way to read the environment, and most C +/// libraries, including libc itself, do not advertise which functions read +/// from the environment. Even functions from the Rust standard library may +/// read the environment without going through this module, e.g. for DNS +/// lookups from [`std::net::ToSocketAddrs`]. No stable guarantee is made about +/// which functions may read from the environment in future versions of a +/// library. All this makes it not practically possible for you to guarantee +/// that no other thread will read the environment, so the only safe option is +/// to not use `set_var` or `remove_var` in multi-threaded programs at all. /// /// Discussion of this unsafety on Unix may be found in: /// @@ -353,16 +350,16 @@ impl Error for VarError { /// use std::env; /// /// let key = "KEY"; -/// env::set_var(key, "VALUE"); +/// unsafe { +/// env::set_var(key, "VALUE"); +/// } /// assert_eq!(env::var(key), Ok("VALUE".to_string())); /// ``` +#[rustc_deprecated_safe_2024] #[stable(feature = "env", since = "1.0.0")] -pub fn set_var, V: AsRef>(key: K, value: V) { - _set_var(key.as_ref(), value.as_ref()) -} - -fn _set_var(key: &OsStr, value: &OsStr) { - os_imp::setenv(key, value).unwrap_or_else(|e| { +pub unsafe fn set_var, V: AsRef>(key: K, value: V) { + let (key, value) = (key.as_ref(), value.as_ref()); + unsafe { os_imp::setenv(key, value) }.unwrap_or_else(|e| { panic!("failed to set environment variable `{key:?}` to `{value:?}`: {e}") }) } @@ -371,22 +368,27 @@ fn _set_var(key: &OsStr, value: &OsStr) { /// /// # Safety /// -/// Even though this function is currently not marked as `unsafe`, it needs to -/// be because invoking it can cause undefined behaviour. The function will be -/// marked `unsafe` in a future version of Rust. This is tracked in -/// [rust#27970](https://github.com/rust-lang/rust/issues/27970). -/// /// This function is safe to call in a single-threaded program. /// -/// In multi-threaded programs, you must ensure that are no other threads -/// concurrently writing or *reading*(!) from the environment through functions -/// other than the ones in this module. You are responsible for figuring out -/// how to achieve this, but we strongly suggest not using `set_var` or -/// `remove_var` in multi-threaded programs at all. -/// -/// Most C libraries, including libc itself do not advertise which functions -/// read from the environment. Even functions from the Rust standard library do -/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`]. +/// This function is also always safe to call on Windows, in single-threaded +/// and multi-threaded programs. +/// +/// In multi-threaded programs on other operating systems, the only safe option is +/// to not use `set_var` or `remove_var` at all. +/// +/// The exact requirement is: you +/// must ensure that there are no other threads concurrently writing or +/// *reading*(!) the environment through functions or global variables other +/// than the ones in this module. The problem is that these operating systems +/// do not provide a thread-safe way to read the environment, and most C +/// libraries, including libc itself, do not advertise which functions read +/// from the environment. Even functions from the Rust standard library may +/// read the environment without going through this module, e.g. for DNS +/// lookups from [`std::net::ToSocketAddrs`]. No stable guarantee is made about +/// which functions may read from the environment in future versions of a +/// library. All this makes it not practically possible for you to guarantee +/// that no other thread will read the environment, so the only safe option is +/// to not use `set_var` or `remove_var` in multi-threaded programs at all. /// /// Discussion of this unsafety on Unix may be found in: /// @@ -403,23 +405,25 @@ fn _set_var(key: &OsStr, value: &OsStr) { /// /// # Examples /// -/// ``` +/// ```no_run /// use std::env; /// /// let key = "KEY"; -/// env::set_var(key, "VALUE"); +/// unsafe { +/// env::set_var(key, "VALUE"); +/// } /// assert_eq!(env::var(key), Ok("VALUE".to_string())); /// -/// env::remove_var(key); +/// unsafe { +/// env::remove_var(key); +/// } /// assert!(env::var(key).is_err()); /// ``` +#[rustc_deprecated_safe_2024] #[stable(feature = "env", since = "1.0.0")] -pub fn remove_var>(key: K) { - _remove_var(key.as_ref()) -} - -fn _remove_var(key: &OsStr) { - os_imp::unsetenv(key) +pub unsafe fn remove_var>(key: K) { + let key = key.as_ref(); + unsafe { os_imp::unsetenv(key) } .unwrap_or_else(|e| panic!("failed to remove environment variable `{key:?}`: {e}")) } diff --git a/library/std/src/error.rs b/library/std/src/error.rs index b240e4e2c45be..3e17431af45b0 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -4,14 +4,14 @@ #[cfg(test)] mod tests; -use crate::backtrace::Backtrace; -use crate::fmt::{self, Write}; - #[stable(feature = "rust1", since = "1.0.0")] pub use core::error::Error; #[unstable(feature = "error_generic_member_access", issue = "99301")] pub use core::error::{request_ref, request_value, Request}; +use crate::backtrace::Backtrace; +use crate::fmt::{self, Write}; + /// An error reporter that prints an error and its sources. /// /// Report also exposes configuration options for formatting the error sources, either entirely on a @@ -234,7 +234,7 @@ impl Report where Report: From, { - /// Create a new `Report` from an input error. + /// Creates a new `Report` from an input error. #[unstable(feature = "error_reporter", issue = "90172")] pub fn new(error: E) -> Report { Self::from(error) @@ -429,7 +429,7 @@ impl Report { /// 1: rust_out::main::_doctest_main_src_error_rs_1158_0 /// 2: rust_out::main /// 3: core::ops::function::FnOnce::call_once - /// 4: std::sys_common::backtrace::__rust_begin_short_backtrace + /// 4: std::sys::backtrace::__rust_begin_short_backtrace /// 5: std::rt::lang_start::{{closure}} /// 6: std::panicking::try /// 7: std::rt::lang_start_internal @@ -500,13 +500,8 @@ where } if self.show_backtrace { - let backtrace = self.backtrace(); - - if let Some(backtrace) = backtrace { - let backtrace = backtrace.to_string(); - - f.write_str("\n\nStack backtrace:\n")?; - f.write_str(backtrace.trim_end())?; + if let Some(backtrace) = self.backtrace() { + write!(f, "\n\nStack backtrace:\n{}", backtrace.to_string().trim_end())?; } } diff --git a/library/std/src/error/tests.rs b/library/std/src/error/tests.rs index ed070a26b0cf0..88a9f33c07908 100644 --- a/library/std/src/error/tests.rs +++ b/library/std/src/error/tests.rs @@ -1,6 +1,7 @@ +use core::error::Request; + use super::Error; use crate::fmt; -use core::error::Request; #[derive(Debug, PartialEq)] struct A; diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index 491235a872eaf..a5b00d57cefdd 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -7,12 +7,12 @@ #[cfg(test)] mod tests; -#[cfg(not(test))] -use crate::intrinsics; - #[unstable(feature = "f128", issue = "116909")] pub use core::f128::consts; +#[cfg(not(test))] +use crate::intrinsics; + #[cfg(not(test))] impl f128 { /// Raises a number to an integer power. @@ -32,4 +32,33 @@ impl f128 { pub fn powi(self, n: i32) -> f128 { unsafe { intrinsics::powif128(self, n) } } + + /// Computes the absolute value of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128)] { // FIXME(f16_f128): reliable_f128 + /// + /// let x = 3.5_f128; + /// let y = -3.5_f128; + /// + /// assert_eq!(x.abs(), x); + /// assert_eq!(y.abs(), -y); + /// + /// assert!(f128::NAN.abs().is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn abs(self) -> Self { + // FIXME(f16_f128): replace with `intrinsics::fabsf128` when available + // We don't do this now because LLVM has lowering bugs for f128 math. + Self::from_bits(self.to_bits() & !(1 << 127)) + } } diff --git a/library/std/src/f128/tests.rs b/library/std/src/f128/tests.rs index b64c7f856a15f..162c8dbad81a1 100644 --- a/library/std/src/f128/tests.rs +++ b/library/std/src/f128/tests.rs @@ -1,29 +1,30 @@ -#![allow(dead_code)] // FIXME(f16_f128): remove once constants are used +// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy +#![cfg(reliable_f128)] + +use crate::f128::consts; +use crate::num::{FpCategory as Fp, *}; /// Smallest number const TINY_BITS: u128 = 0x1; + /// Next smallest number const TINY_UP_BITS: u128 = 0x2; + /// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u128 = 0x7ffeffffffffffffffffffffffffffff; +const MAX_DOWN_BITS: u128 = 0x7ffefffffffffffffffffffffffffffe; + /// Zeroed exponent, full significant const LARGEST_SUBNORMAL_BITS: u128 = 0x0000ffffffffffffffffffffffffffff; + /// Exponent = 0b1, zeroed significand const SMALLEST_NORMAL_BITS: u128 = 0x00010000000000000000000000000000; + /// First pattern over the mantissa const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; + /// Second pattern over the mantissa const NAN_MASK2: u128 = 0x00005555555555555555555555555555; -/// Compare by value -#[allow(unused_macros)] -macro_rules! assert_f128_eq { - ($a:expr, $b:expr) => { - let (l, r): (&f128, &f128) = (&$a, &$b); - assert_eq!(*l, *r, "\na: {:#0130x}\nb: {:#0130x}", l.to_bits(), r.to_bits()) - }; -} - /// Compare by representation #[allow(unused_macros)] macro_rules! assert_f128_biteq { @@ -31,10 +32,530 @@ macro_rules! assert_f128_biteq { let (l, r): (&f128, &f128) = (&$a, &$b); let lb = l.to_bits(); let rb = r.to_bits(); - assert_eq!( - lb, rb, - "float {:?} is not bitequal to {:?}.\na: {:#0130x}\nb: {:#0130x}", - *l, *r, lb, rb - ); + assert_eq!(lb, rb, "float {l:?} is not bitequal to {r:?}.\na: {lb:#034x}\nb: {rb:#034x}"); }; } + +#[test] +fn test_num_f128() { + test_num(10f128, 2f128); +} + +// FIXME(f16_f128): add min and max tests when available + +#[test] +fn test_nan() { + let nan: f128 = f128::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert!(!nan.is_normal()); + assert_eq!(Fp::Nan, nan.classify()); +} + +#[test] +fn test_infinity() { + let inf: f128 = f128::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f128 = 0.0f128; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f128 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f128 = 1.0f128; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f128.is_nan()); + assert!(!5.3f128.is_nan()); + assert!(!(-10.732f128).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f128.is_infinite()); + assert!(!42.8f128.is_infinite()); + assert!(!(-109.2f128).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f128.is_finite()); + assert!(42.8f128.is_finite()); + assert!((-109.2f128).is_finite()); +} + +#[test] +fn test_is_normal() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let zero: f128 = 0.0f128; + let neg_zero: f128 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f128.is_normal()); + assert!(1e-4931f128.is_normal()); + assert!(!1e-4932f128.is_normal()); +} + +#[test] +fn test_classify() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let zero: f128 = 0.0f128; + let neg_zero: f128 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1f128.classify(), Fp::Normal); + assert_eq!(1e-4931f128.classify(), Fp::Normal); + assert_eq!(1e-4932f128.classify(), Fp::Subnormal); +} + +// FIXME(f16_f128): add missing math functions when available + +#[test] +fn test_abs() { + assert_eq!(f128::INFINITY.abs(), f128::INFINITY); + assert_eq!(1f128.abs(), 1f128); + assert_eq!(0f128.abs(), 0f128); + assert_eq!((-0f128).abs(), 0f128); + assert_eq!((-1f128).abs(), 1f128); + assert_eq!(f128::NEG_INFINITY.abs(), f128::INFINITY); + assert_eq!((1f128 / f128::NEG_INFINITY).abs(), 0f128); + assert!(f128::NAN.abs().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f128::INFINITY.is_sign_positive()); + assert!(1f128.is_sign_positive()); + assert!(0f128.is_sign_positive()); + assert!(!(-0f128).is_sign_positive()); + assert!(!(-1f128).is_sign_positive()); + assert!(!f128::NEG_INFINITY.is_sign_positive()); + assert!(!(1f128 / f128::NEG_INFINITY).is_sign_positive()); + assert!(f128::NAN.is_sign_positive()); + assert!(!(-f128::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f128::INFINITY.is_sign_negative()); + assert!(!1f128.is_sign_negative()); + assert!(!0f128.is_sign_negative()); + assert!((-0f128).is_sign_negative()); + assert!((-1f128).is_sign_negative()); + assert!(f128::NEG_INFINITY.is_sign_negative()); + assert!((1f128 / f128::NEG_INFINITY).is_sign_negative()); + assert!(!f128::NAN.is_sign_negative()); + assert!((-f128::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f128::from_bits(TINY_BITS); + let tiny_up = f128::from_bits(TINY_UP_BITS); + let max_down = f128::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); + assert_f128_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN); + assert_f128_biteq!(f128::MIN.next_up(), -max_down); + assert_f128_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0); + assert_f128_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f128_biteq!((-tiny_up).next_up(), -tiny); + assert_f128_biteq!((-tiny).next_up(), -0.0f128); + assert_f128_biteq!((-0.0f128).next_up(), tiny); + assert_f128_biteq!(0.0f128.next_up(), tiny); + assert_f128_biteq!(tiny.next_up(), tiny_up); + assert_f128_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f128_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON); + assert_f128_biteq!(f128::MAX.next_up(), f128::INFINITY); + assert_f128_biteq!(f128::INFINITY.next_up(), f128::INFINITY); + + // Check that NaNs roundtrip. + let nan0 = f128::NAN; + let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); + assert_f128_biteq!(nan0.next_up(), nan0); + assert_f128_biteq!(nan1.next_up(), nan1); + assert_f128_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f128::from_bits(TINY_BITS); + let tiny_up = f128::from_bits(TINY_UP_BITS); + let max_down = f128::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); + assert_f128_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY); + assert_f128_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY); + assert_f128_biteq!((-max_down).next_down(), f128::MIN); + assert_f128_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON); + assert_f128_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f128_biteq!((-tiny).next_down(), -tiny_up); + assert_f128_biteq!((-0.0f128).next_down(), -tiny); + assert_f128_biteq!((0.0f128).next_down(), -tiny); + assert_f128_biteq!(tiny.next_down(), 0.0f128); + assert_f128_biteq!(tiny_up.next_down(), tiny); + assert_f128_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f128_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128); + assert_f128_biteq!(f128::MAX.next_down(), max_down); + assert_f128_biteq!(f128::INFINITY.next_down(), f128::MAX); + + // Check that NaNs roundtrip. + let nan0 = f128::NAN; + let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); + assert_f128_biteq!(nan0.next_down(), nan0); + assert_f128_biteq!(nan1.next_down(), nan1); + assert_f128_biteq!(nan2.next_down(), nan2); +} + +#[test] +fn test_recip() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.recip(), 1.0); + assert_eq!(2.0f128.recip(), 0.5); + assert_eq!((-0.4f128).recip(), -2.5); + assert_eq!(0.0f128.recip(), inf); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +fn test_to_degrees() { + let pi: f128 = consts::PI; + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(0.0f128.to_degrees(), 0.0); + assert_approx_eq!((-5.8f128).to_degrees(), -332.315521); + assert_eq!(pi.to_degrees(), 180.0); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); + assert_eq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703); +} + +#[test] +fn test_to_radians() { + let pi: f128 = consts::PI; + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(0.0f128.to_radians(), 0.0); + assert_approx_eq!(154.6f128.to_radians(), 2.698279); + assert_approx_eq!((-332.31f128).to_radians(), -5.799903); + // check approx rather than exact because round trip for pi doesn't fall on an exactly + // representable value (unlike `f32` and `f64`). + assert_approx_eq!(180.0f128.to_radians(), pi); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_real_consts() { + // FIXME(f16_f128): add math tests when available + use super::consts; + + let pi: f128 = consts::PI; + let frac_pi_2: f128 = consts::FRAC_PI_2; + let frac_pi_3: f128 = consts::FRAC_PI_3; + let frac_pi_4: f128 = consts::FRAC_PI_4; + let frac_pi_6: f128 = consts::FRAC_PI_6; + let frac_pi_8: f128 = consts::FRAC_PI_8; + let frac_1_pi: f128 = consts::FRAC_1_PI; + let frac_2_pi: f128 = consts::FRAC_2_PI; + // let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; + // let sqrt2: f128 = consts::SQRT_2; + // let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; + // let e: f128 = consts::E; + // let log2_e: f128 = consts::LOG2_E; + // let log10_e: f128 = consts::LOG10_E; + // let ln_2: f128 = consts::LN_2; + // let ln_10: f128 = consts::LN_10; + + assert_approx_eq!(frac_pi_2, pi / 2f128); + assert_approx_eq!(frac_pi_3, pi / 3f128); + assert_approx_eq!(frac_pi_4, pi / 4f128); + assert_approx_eq!(frac_pi_6, pi / 6f128); + assert_approx_eq!(frac_pi_8, pi / 8f128); + assert_approx_eq!(frac_1_pi, 1f128 / pi); + assert_approx_eq!(frac_2_pi, 2f128 / pi); + // assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt()); + // assert_approx_eq!(sqrt2, 2f128.sqrt()); + // assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt()); + // assert_approx_eq!(log2_e, e.log2()); + // assert_approx_eq!(log10_e, e.log10()); + // assert_approx_eq!(ln_2, 2f128.ln()); + // assert_approx_eq!(ln_10, 10f128.ln()); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f128).to_bits(), 0x3fff0000000000000000000000000000); + assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); + assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); + assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); + assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); + assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5); + assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); + assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + let masked_nan1 = f128::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f128::NAN.to_bits() ^ NAN_MASK2; + assert!(f128::from_bits(masked_nan1).is_nan()); + assert!(f128::from_bits(masked_nan2).is_nan()); + + assert_eq!(f128::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f128.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f128.clamp(f128::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f128.clamp(3.0, f128::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u128 { + 1 << (f128::MANTISSA_DIGITS - 2) + } + + // FIXME(f16_f128): test subnormals when powf is available + // fn min_subnorm() -> f128 { + // f128::MIN_POSITIVE / f128::powf(2.0, f128::MANTISSA_DIGITS as f128 - 1.0) + // } + + // fn max_subnorm() -> f128 { + // f128::MIN_POSITIVE - min_subnorm() + // } + + fn q_nan() -> f128 { + f128::from_bits(f128::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f128 { + f128::from_bits((f128::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f128::INFINITY).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Equal, (-f128::MAX).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f128).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f128).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f128::MIN_POSITIVE).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f128).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f128.total_cmp(&0.0)); + // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f128::MIN_POSITIVE.total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f128.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f128.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f128::MAX.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Equal, f128::INFINITY.total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-f128::INFINITY).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-f128::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f128).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f128).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-f128::MIN_POSITIVE).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f128).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, 0.0_f128.total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f128::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f128.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f128.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, f128::MAX.total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, f128::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f128::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f128::MAX).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f128).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f128).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f128::MIN_POSITIVE).total_cmp(&-0.5)); + // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Greater, (-0.0_f128).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f128.total_cmp(&-0.0)); + // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Greater, f128::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f128.total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f128.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f128::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f128::INFINITY.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index 1cb655ffabd84..e3024defed734 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -7,12 +7,12 @@ #[cfg(test)] mod tests; -#[cfg(not(test))] -use crate::intrinsics; - #[unstable(feature = "f16", issue = "116909")] pub use core::f16::consts; +#[cfg(not(test))] +use crate::intrinsics; + #[cfg(not(test))] impl f16 { /// Raises a number to an integer power. @@ -32,4 +32,32 @@ impl f16 { pub fn powi(self, n: i32) -> f16 { unsafe { intrinsics::powif16(self, n) } } + + /// Computes the absolute value of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16)] { + /// + /// let x = 3.5_f16; + /// let y = -3.5_f16; + /// + /// assert_eq!(x.abs(), x); + /// assert_eq!(y.abs(), -y); + /// + /// assert!(f16::NAN.abs().is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn abs(self) -> Self { + // FIXME(f16_f128): replace with `intrinsics::fabsf16` when available + Self::from_bits(self.to_bits() & !(1 << 15)) + } } diff --git a/library/std/src/f16/tests.rs b/library/std/src/f16/tests.rs index d65c43eca4bb8..f73bdf68e8295 100644 --- a/library/std/src/f16/tests.rs +++ b/library/std/src/f16/tests.rs @@ -1,35 +1,36 @@ -#![allow(dead_code)] // FIXME(f16_f128): remove once constants are used +// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy +#![cfg(reliable_f16)] + +use crate::f16::consts; +use crate::num::{FpCategory as Fp, *}; // We run out of precision pretty quickly with f16 -const F16_APPROX_L1: f16 = 0.001; +// const F16_APPROX_L1: f16 = 0.001; const F16_APPROX_L2: f16 = 0.01; -const F16_APPROX_L3: f16 = 0.1; +// const F16_APPROX_L3: f16 = 0.1; const F16_APPROX_L4: f16 = 0.5; /// Smallest number const TINY_BITS: u16 = 0x1; + /// Next smallest number const TINY_UP_BITS: u16 = 0x2; + /// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 const MAX_DOWN_BITS: u16 = 0x7bfe; + /// Zeroed exponent, full significant const LARGEST_SUBNORMAL_BITS: u16 = 0x03ff; + /// Exponent = 0b1, zeroed significand const SMALLEST_NORMAL_BITS: u16 = 0x0400; + /// First pattern over the mantissa const NAN_MASK1: u16 = 0x02aa; + /// Second pattern over the mantissa const NAN_MASK2: u16 = 0x0155; -/// Compare by value -#[allow(unused_macros)] -macro_rules! assert_f16_eq { - ($a:expr, $b:expr) => { - let (l, r): (&f16, &f16) = (&$a, &$b); - assert_eq!(*l, *r, "\na: {:#018x}\nb: {:#018x}", l.to_bits(), r.to_bits()) - }; -} - /// Compare by representation #[allow(unused_macros)] macro_rules! assert_f16_biteq { @@ -37,10 +38,527 @@ macro_rules! assert_f16_biteq { let (l, r): (&f16, &f16) = (&$a, &$b); let lb = l.to_bits(); let rb = r.to_bits(); - assert_eq!( - lb, rb, - "float {:?} is not bitequal to {:?}.\na: {:#018x}\nb: {:#018x}", - *l, *r, lb, rb - ); + assert_eq!(lb, rb, "float {l:?} ({lb:#04x}) is not bitequal to {r:?} ({rb:#04x})"); }; } + +#[test] +fn test_num_f16() { + test_num(10f16, 2f16); +} + +// FIXME(f16_f128): add min and max tests when available + +#[test] +fn test_nan() { + let nan: f16 = f16::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert!(!nan.is_normal()); + assert_eq!(Fp::Nan, nan.classify()); +} + +#[test] +fn test_infinity() { + let inf: f16 = f16::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f16 = 0.0f16; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f16 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f16 = 1.0f16; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f16.is_nan()); + assert!(!5.3f16.is_nan()); + assert!(!(-10.732f16).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f16.is_infinite()); + assert!(!42.8f16.is_infinite()); + assert!(!(-109.2f16).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f16.is_finite()); + assert!(42.8f16.is_finite()); + assert!((-109.2f16).is_finite()); +} + +#[test] +fn test_is_normal() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let zero: f16 = 0.0f16; + let neg_zero: f16 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f16.is_normal()); + assert!(1e-4f16.is_normal()); + assert!(!1e-5f16.is_normal()); +} + +#[test] +fn test_classify() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let zero: f16 = 0.0f16; + let neg_zero: f16 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1f16.classify(), Fp::Normal); + assert_eq!(1e-4f16.classify(), Fp::Normal); + assert_eq!(1e-5f16.classify(), Fp::Subnormal); +} + +// FIXME(f16_f128): add missing math functions when available + +#[test] +fn test_abs() { + assert_eq!(f16::INFINITY.abs(), f16::INFINITY); + assert_eq!(1f16.abs(), 1f16); + assert_eq!(0f16.abs(), 0f16); + assert_eq!((-0f16).abs(), 0f16); + assert_eq!((-1f16).abs(), 1f16); + assert_eq!(f16::NEG_INFINITY.abs(), f16::INFINITY); + assert_eq!((1f16 / f16::NEG_INFINITY).abs(), 0f16); + assert!(f16::NAN.abs().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f16::INFINITY.is_sign_positive()); + assert!(1f16.is_sign_positive()); + assert!(0f16.is_sign_positive()); + assert!(!(-0f16).is_sign_positive()); + assert!(!(-1f16).is_sign_positive()); + assert!(!f16::NEG_INFINITY.is_sign_positive()); + assert!(!(1f16 / f16::NEG_INFINITY).is_sign_positive()); + assert!(f16::NAN.is_sign_positive()); + assert!(!(-f16::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f16::INFINITY.is_sign_negative()); + assert!(!1f16.is_sign_negative()); + assert!(!0f16.is_sign_negative()); + assert!((-0f16).is_sign_negative()); + assert!((-1f16).is_sign_negative()); + assert!(f16::NEG_INFINITY.is_sign_negative()); + assert!((1f16 / f16::NEG_INFINITY).is_sign_negative()); + assert!(!f16::NAN.is_sign_negative()); + assert!((-f16::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f16::from_bits(TINY_BITS); + let tiny_up = f16::from_bits(TINY_UP_BITS); + let max_down = f16::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); + assert_f16_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN); + assert_f16_biteq!(f16::MIN.next_up(), -max_down); + assert_f16_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0); + assert_f16_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f16_biteq!((-tiny_up).next_up(), -tiny); + assert_f16_biteq!((-tiny).next_up(), -0.0f16); + assert_f16_biteq!((-0.0f16).next_up(), tiny); + assert_f16_biteq!(0.0f16.next_up(), tiny); + assert_f16_biteq!(tiny.next_up(), tiny_up); + assert_f16_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f16_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON); + assert_f16_biteq!(f16::MAX.next_up(), f16::INFINITY); + assert_f16_biteq!(f16::INFINITY.next_up(), f16::INFINITY); + + // Check that NaNs roundtrip. + let nan0 = f16::NAN; + let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); + assert_f16_biteq!(nan0.next_up(), nan0); + assert_f16_biteq!(nan1.next_up(), nan1); + assert_f16_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f16::from_bits(TINY_BITS); + let tiny_up = f16::from_bits(TINY_UP_BITS); + let max_down = f16::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); + assert_f16_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY); + assert_f16_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY); + assert_f16_biteq!((-max_down).next_down(), f16::MIN); + assert_f16_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON); + assert_f16_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f16_biteq!((-tiny).next_down(), -tiny_up); + assert_f16_biteq!((-0.0f16).next_down(), -tiny); + assert_f16_biteq!((0.0f16).next_down(), -tiny); + assert_f16_biteq!(tiny.next_down(), 0.0f16); + assert_f16_biteq!(tiny_up.next_down(), tiny); + assert_f16_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f16_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16); + assert_f16_biteq!(f16::MAX.next_down(), max_down); + assert_f16_biteq!(f16::INFINITY.next_down(), f16::MAX); + + // Check that NaNs roundtrip. + let nan0 = f16::NAN; + let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); + assert_f16_biteq!(nan0.next_down(), nan0); + assert_f16_biteq!(nan1.next_down(), nan1); + assert_f16_biteq!(nan2.next_down(), nan2); +} + +#[test] +fn test_recip() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(1.0f16.recip(), 1.0); + assert_eq!(2.0f16.recip(), 0.5); + assert_eq!((-0.4f16).recip(), -2.5); + assert_eq!(0.0f16.recip(), inf); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +fn test_to_degrees() { + let pi: f16 = consts::PI; + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(0.0f16.to_degrees(), 0.0); + assert_approx_eq!((-5.8f16).to_degrees(), -332.315521); + assert_approx_eq!(pi.to_degrees(), 180.0, F16_APPROX_L4); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); + assert_eq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703); +} + +#[test] +fn test_to_radians() { + let pi: f16 = consts::PI; + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(0.0f16.to_radians(), 0.0); + assert_approx_eq!(154.6f16.to_radians(), 2.698279); + assert_approx_eq!((-332.31f16).to_radians(), -5.799903); + assert_approx_eq!(180.0f16.to_radians(), pi, F16_APPROX_L2); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_real_consts() { + // FIXME(f16_f128): add math tests when available + use super::consts; + + let pi: f16 = consts::PI; + let frac_pi_2: f16 = consts::FRAC_PI_2; + let frac_pi_3: f16 = consts::FRAC_PI_3; + let frac_pi_4: f16 = consts::FRAC_PI_4; + let frac_pi_6: f16 = consts::FRAC_PI_6; + let frac_pi_8: f16 = consts::FRAC_PI_8; + let frac_1_pi: f16 = consts::FRAC_1_PI; + let frac_2_pi: f16 = consts::FRAC_2_PI; + // let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; + // let sqrt2: f16 = consts::SQRT_2; + // let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; + // let e: f16 = consts::E; + // let log2_e: f16 = consts::LOG2_E; + // let log10_e: f16 = consts::LOG10_E; + // let ln_2: f16 = consts::LN_2; + // let ln_10: f16 = consts::LN_10; + + assert_approx_eq!(frac_pi_2, pi / 2f16); + assert_approx_eq!(frac_pi_3, pi / 3f16); + assert_approx_eq!(frac_pi_4, pi / 4f16); + assert_approx_eq!(frac_pi_6, pi / 6f16); + assert_approx_eq!(frac_pi_8, pi / 8f16); + assert_approx_eq!(frac_1_pi, 1f16 / pi); + assert_approx_eq!(frac_2_pi, 2f16 / pi); + // assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt()); + // assert_approx_eq!(sqrt2, 2f16.sqrt()); + // assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt()); + // assert_approx_eq!(log2_e, e.log2()); + // assert_approx_eq!(log10_e, e.log10()); + // assert_approx_eq!(ln_2, 2f16.ln()); + // assert_approx_eq!(ln_10, 10f16.ln()); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f16).to_bits(), 0x3c00); + assert_eq!((12.5f16).to_bits(), 0x4a40); + assert_eq!((1337f16).to_bits(), 0x6539); + assert_eq!((-14.25f16).to_bits(), 0xcb20); + assert_approx_eq!(f16::from_bits(0x3c00), 1.0); + assert_approx_eq!(f16::from_bits(0x4a40), 12.5); + assert_approx_eq!(f16::from_bits(0x6539), 1337.0); + assert_approx_eq!(f16::from_bits(0xcb20), -14.25); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f16::NAN.to_bits() ^ NAN_MASK2; + assert!(f16::from_bits(masked_nan1).is_nan()); + assert!(f16::from_bits(masked_nan2).is_nan()); + + assert_eq!(f16::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f16.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f16.clamp(f16::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f16.clamp(3.0, f16::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u16 { + 1 << (f16::MANTISSA_DIGITS - 2) + } + + // FIXME(f16_f128): test subnormals when powf is available + // fn min_subnorm() -> f16 { + // f16::MIN_POSITIVE / f16::powf(2.0, f16::MANTISSA_DIGITS as f16 - 1.0) + // } + + // fn max_subnorm() -> f16 { + // f16::MIN_POSITIVE - min_subnorm() + // } + + fn q_nan() -> f16 { + f16::from_bits(f16::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f16 { + f16::from_bits((f16::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f16::INFINITY).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Equal, (-f16::MAX).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f16).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f16).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f16::MIN_POSITIVE).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f16).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f16.total_cmp(&0.0)); + // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f16::MIN_POSITIVE.total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f16.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f16.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f16::MAX.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Equal, f16::INFINITY.total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-f16::INFINITY).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-f16::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f16).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f16).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-f16::MIN_POSITIVE).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f16).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, 0.0_f16.total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f16::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f16.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f16.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, f16::MAX.total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, f16::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f16::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f16::MAX).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f16).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f16).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f16::MIN_POSITIVE).total_cmp(&-0.5)); + // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Greater, (-0.0_f16).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f16.total_cmp(&-0.0)); + // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Greater, f16::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f16.total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f16.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f16::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f16::INFINITY.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 4fc82fec0adbc..12433d25bfa45 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -15,11 +15,6 @@ #[cfg(test)] mod tests; -#[cfg(not(test))] -use crate::intrinsics; -#[cfg(not(test))] -use crate::sys::cmath; - #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::f32::{ @@ -27,6 +22,11 @@ pub use core::f32::{ MIN_EXP, MIN_POSITIVE, NAN, NEG_INFINITY, RADIX, }; +#[cfg(not(test))] +use crate::intrinsics; +#[cfg(not(test))] +use crate::sys::cmath; + #[cfg(not(test))] impl f32 { /// Returns the largest integer less than or equal to `self`. @@ -574,7 +574,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f32 { - crate::sys::log2f32(self) + unsafe { intrinsics::log2f32(self) } } /// Returns the base 10 logarithm of the number. diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index 9ca4e8f2f45fe..3a4c1c120a495 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -1,6 +1,44 @@ use crate::f32::consts; -use crate::num::FpCategory as Fp; -use crate::num::*; +use crate::num::{FpCategory as Fp, *}; + +/// Smallest number +#[allow(dead_code)] // unused on x86 +const TINY_BITS: u32 = 0x1; + +/// Next smallest number +#[allow(dead_code)] // unused on x86 +const TINY_UP_BITS: u32 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +#[allow(dead_code)] // unused on x86 +const MAX_DOWN_BITS: u32 = 0x7f7f_fffe; + +/// Zeroed exponent, full significant +#[allow(dead_code)] // unused on x86 +const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff; + +/// Exponent = 0b1, zeroed significand +#[allow(dead_code)] // unused on x86 +const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000; + +/// First pattern over the mantissa +#[allow(dead_code)] // unused on x86 +const NAN_MASK1: u32 = 0x002a_aaaa; + +/// Second pattern over the mantissa +#[allow(dead_code)] // unused on x86 +const NAN_MASK2: u32 = 0x0055_5555; + +#[allow(unused_macros)] +macro_rules! assert_f32_biteq { + ($left : expr, $right : expr) => { + let l: &f32 = &$left; + let r: &f32 = &$right; + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l} ({lb:#010x}) is not bitequal to {r} ({rb:#010x})"); + }; +} #[test] fn test_num_f32() { @@ -315,27 +353,16 @@ fn test_is_sign_negative() { assert!((-f32::NAN).is_sign_negative()); } -#[allow(unused_macros)] -macro_rules! assert_f32_biteq { - ($left : expr, $right : expr) => { - let l: &f32 = &$left; - let r: &f32 = &$right; - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb); - }; -} - // Ignore test on x87 floating point, these platforms do not guarantee NaN // payloads are preserved and flush denormals to zero, failing the tests. #[cfg(not(target_arch = "x86"))] #[test] fn test_next_up() { - let tiny = f32::from_bits(1); - let tiny_up = f32::from_bits(2); - let max_down = f32::from_bits(0x7f7f_fffe); - let largest_subnormal = f32::from_bits(0x007f_ffff); - let smallest_normal = f32::from_bits(0x0080_0000); + let tiny = f32::from_bits(TINY_BITS); + let tiny_up = f32::from_bits(TINY_UP_BITS); + let max_down = f32::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN); assert_f32_biteq!(f32::MIN.next_up(), -max_down); assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0); @@ -352,8 +379,8 @@ fn test_next_up() { // Check that NaNs roundtrip. let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); assert_f32_biteq!(nan0.next_up(), nan0); assert_f32_biteq!(nan1.next_up(), nan1); assert_f32_biteq!(nan2.next_up(), nan2); @@ -364,11 +391,11 @@ fn test_next_up() { #[cfg(not(target_arch = "x86"))] #[test] fn test_next_down() { - let tiny = f32::from_bits(1); - let tiny_up = f32::from_bits(2); - let max_down = f32::from_bits(0x7f7f_fffe); - let largest_subnormal = f32::from_bits(0x007f_ffff); - let smallest_normal = f32::from_bits(0x0080_0000); + let tiny = f32::from_bits(TINY_BITS); + let tiny_up = f32::from_bits(TINY_UP_BITS); + let max_down = f32::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY); assert_f32_biteq!((-max_down).next_down(), f32::MIN); @@ -386,8 +413,8 @@ fn test_next_down() { // Check that NaNs roundtrip. let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ 0x0055_5555); + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); assert_f32_biteq!(nan0.next_down(), nan0); assert_f32_biteq!(nan1.next_down(), nan1); assert_f32_biteq!(nan2.next_down(), nan2); @@ -734,8 +761,8 @@ fn test_float_bits_conv() { // Check that NaNs roundtrip their bits regardless of signaling-ness // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits - let masked_nan1 = f32::NAN.to_bits() ^ 0x002A_AAAA; - let masked_nan2 = f32::NAN.to_bits() ^ 0x0055_5555; + let masked_nan1 = f32::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2; assert!(f32::from_bits(masked_nan1).is_nan()); assert!(f32::from_bits(masked_nan2).is_nan()); diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index f8c66a3e71752..a343e19173e59 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -15,11 +15,6 @@ #[cfg(test)] mod tests; -#[cfg(not(test))] -use crate::intrinsics; -#[cfg(not(test))] -use crate::sys::cmath; - #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::f64::{ @@ -27,6 +22,11 @@ pub use core::f64::{ MIN_EXP, MIN_POSITIVE, NAN, NEG_INFINITY, RADIX, }; +#[cfg(not(test))] +use crate::intrinsics; +#[cfg(not(test))] +use crate::sys::cmath; + #[cfg(not(test))] impl f64 { /// Returns the largest integer less than or equal to `self`. @@ -520,7 +520,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn ln(self) -> f64 { - crate::sys::log_wrapper(self, |n| unsafe { intrinsics::logf64(n) }) + unsafe { intrinsics::logf64(self) } } /// Returns the logarithm of the number with respect to an arbitrary base. @@ -574,7 +574,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f64 { - crate::sys::log_wrapper(self, crate::sys::log2f64) + unsafe { intrinsics::log2f64(self) } } /// Returns the base 10 logarithm of the number. @@ -599,7 +599,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log10(self) -> f64 { - crate::sys::log_wrapper(self, |n| unsafe { intrinsics::log10f64(n) }) + unsafe { intrinsics::log10f64(self) } } /// The positive difference of two numbers. diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index f88d01593b5e4..bac8405f97361 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -1,6 +1,44 @@ use crate::f64::consts; -use crate::num::FpCategory as Fp; -use crate::num::*; +use crate::num::{FpCategory as Fp, *}; + +/// Smallest number +#[allow(dead_code)] // unused on x86 +const TINY_BITS: u64 = 0x1; + +/// Next smallest number +#[allow(dead_code)] // unused on x86 +const TINY_UP_BITS: u64 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +#[allow(dead_code)] // unused on x86 +const MAX_DOWN_BITS: u64 = 0x7fef_ffff_ffff_fffe; + +/// Zeroed exponent, full significant +#[allow(dead_code)] // unused on x86 +const LARGEST_SUBNORMAL_BITS: u64 = 0x000f_ffff_ffff_ffff; + +/// Exponent = 0b1, zeroed significand +#[allow(dead_code)] // unused on x86 +const SMALLEST_NORMAL_BITS: u64 = 0x0010_0000_0000_0000; + +/// First pattern over the mantissa +#[allow(dead_code)] // unused on x86 +const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; + +/// Second pattern over the mantissa +#[allow(dead_code)] // unused on x86 +const NAN_MASK2: u64 = 0x0005_5555_5555_5555; + +#[allow(unused_macros)] +macro_rules! assert_f64_biteq { + ($left : expr, $right : expr) => { + let l: &f64 = &$left; + let r: &f64 = &$right; + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l} ({lb:#018x}) is not bitequal to {r} ({rb:#018x})"); + }; +} #[test] fn test_num_f64() { @@ -305,27 +343,16 @@ fn test_is_sign_negative() { assert!((-f64::NAN).is_sign_negative()); } -#[allow(unused_macros)] -macro_rules! assert_f64_biteq { - ($left : expr, $right : expr) => { - let l: &f64 = &$left; - let r: &f64 = &$right; - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {} ({:#x}) is not equal to {} ({:#x})", *l, lb, *r, rb); - }; -} - // Ignore test on x87 floating point, these platforms do not guarantee NaN // payloads are preserved and flush denormals to zero, failing the tests. #[cfg(not(target_arch = "x86"))] #[test] fn test_next_up() { - let tiny = f64::from_bits(1); - let tiny_up = f64::from_bits(2); - let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe); - let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff); - let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); + let tiny = f64::from_bits(TINY_BITS); + let tiny_up = f64::from_bits(TINY_UP_BITS); + let max_down = f64::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN); assert_f64_biteq!(f64::MIN.next_up(), -max_down); assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0); @@ -341,8 +368,8 @@ fn test_next_up() { assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY); let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); assert_f64_biteq!(nan0.next_up(), nan0); assert_f64_biteq!(nan1.next_up(), nan1); assert_f64_biteq!(nan2.next_up(), nan2); @@ -353,11 +380,11 @@ fn test_next_up() { #[cfg(not(target_arch = "x86"))] #[test] fn test_next_down() { - let tiny = f64::from_bits(1); - let tiny_up = f64::from_bits(2); - let max_down = f64::from_bits(0x7fef_ffff_ffff_fffe); - let largest_subnormal = f64::from_bits(0x000f_ffff_ffff_ffff); - let smallest_normal = f64::from_bits(0x0010_0000_0000_0000); + let tiny = f64::from_bits(TINY_BITS); + let tiny_up = f64::from_bits(TINY_UP_BITS); + let max_down = f64::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY); assert_f64_biteq!((-max_down).next_down(), f64::MIN); @@ -374,8 +401,8 @@ fn test_next_down() { assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX); let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ 0x000a_aaaa_aaaa_aaaa); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ 0x0005_5555_5555_5555); + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); assert_f64_biteq!(nan0.next_down(), nan0); assert_f64_biteq!(nan1.next_down(), nan1); assert_f64_biteq!(nan2.next_down(), nan2); @@ -715,9 +742,8 @@ fn test_float_bits_conv() { assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25); // Check that NaNs roundtrip their bits regardless of signaling-ness - // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits - let masked_nan1 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA; - let masked_nan2 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555; + let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f64::NAN.to_bits() ^ NAN_MASK2; assert!(f64::from_bits(masked_nan1).is_nan()); assert!(f64::from_bits(masked_nan2).is_nan()); diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs index b59b0c5bba65a..cb0ca5d1376ea 100644 --- a/library/std/src/ffi/c_str.rs +++ b/library/std/src/ffi/c_str.rs @@ -1,19 +1,14 @@ //! [`CStr`], [`CString`], and related types. -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::ffi::c_str::CStr; - -#[stable(feature = "cstr_from_bytes", since = "1.10.0")] -pub use core::ffi::c_str::FromBytesWithNulError; - -#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] -pub use core::ffi::c_str::FromBytesUntilNulError; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc::ffi::c_str::{CString, NulError}; - #[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] pub use alloc::ffi::c_str::FromVecWithNulError; - #[stable(feature = "cstring_into", since = "1.7.0")] pub use alloc::ffi::c_str::IntoStringError; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc::ffi::c_str::{CString, NulError}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::ffi::c_str::CStr; +#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] +pub use core::ffi::c_str::FromBytesUntilNulError; +#[stable(feature = "cstr_from_bytes", since = "1.10.0")] +pub use core::ffi::c_str::FromBytesWithNulError; diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index f45fd77e8b167..2b67750c2f0a9 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -164,50 +164,42 @@ #[unstable(feature = "c_str_module", issue = "112134")] pub mod c_str; -#[doc(inline)] -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::c_str::{CStr, CString}; - -#[doc(no_inline)] -#[stable(feature = "cstr_from_bytes", since = "1.10.0")] -pub use self::c_str::FromBytesWithNulError; +#[stable(feature = "core_c_void", since = "1.30.0")] +pub use core::ffi::c_void; +#[stable(feature = "core_ffi_c", since = "1.64.0")] +pub use core::ffi::{ + c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, + c_ulong, c_ulonglong, c_ushort, +}; +#[unstable( + feature = "c_variadic", + reason = "the `c_variadic` feature has not been properly tested on \ + all supported platforms", + issue = "44930" +)] +pub use core::ffi::{VaList, VaListImpl}; #[doc(no_inline)] #[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] pub use self::c_str::FromBytesUntilNulError; - #[doc(no_inline)] -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::c_str::NulError; - +#[stable(feature = "cstr_from_bytes", since = "1.10.0")] +pub use self::c_str::FromBytesWithNulError; #[doc(no_inline)] #[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] pub use self::c_str::FromVecWithNulError; - #[doc(no_inline)] #[stable(feature = "cstring_into", since = "1.7.0")] pub use self::c_str::IntoStringError; - +#[doc(no_inline)] +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::c_str::NulError; +#[doc(inline)] +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::c_str::{CStr, CString}; #[stable(feature = "rust1", since = "1.0.0")] #[doc(inline)] pub use self::os_str::{OsStr, OsString}; -#[stable(feature = "core_ffi_c", since = "1.64.0")] -pub use core::ffi::{ - c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, - c_ulong, c_ulonglong, c_ushort, -}; - -#[stable(feature = "core_c_void", since = "1.30.0")] -pub use core::ffi::c_void; - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -pub use core::ffi::{VaList, VaListImpl}; - #[unstable(feature = "os_str_display", issue = "120048")] pub mod os_str; diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 9dd3d7d3fa16a..a501bcc98cf38 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -4,18 +4,15 @@ mod tests; use crate::borrow::{Borrow, Cow}; -use crate::cmp; use crate::collections::TryReserveError; -use crate::fmt; use crate::hash::{Hash, Hasher}; use crate::ops::{self, Range}; use crate::rc::Rc; -use crate::slice; use crate::str::FromStr; use crate::sync::Arc; - use crate::sys::os_str::{Buf, Slice}; use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::{cmp, fmt, slice}; /// A type that can represent owned, mutable platform-native strings, but is /// cheaply inter-convertible with Rust strings. @@ -115,10 +112,8 @@ impl crate::sealed::Sealed for OsString {} #[stable(feature = "rust1", since = "1.0.0")] // `OsStr::from_inner` current implementation relies // on `OsStr` being layout-compatible with `Slice`. -// However, `OsStr` layout is considered an implementation detail and must not be relied upon. We -// want `repr(transparent)` but we don't want it to show up in rustdoc, so we hide it under -// `cfg(doc)`. This is an ad-hoc implementation of attribute privacy. -#[cfg_attr(not(doc), repr(transparent))] +// However, `OsStr` layout is considered an implementation detail and must not be relied upon. +#[repr(transparent)] pub struct OsStr { inner: Slice, } @@ -184,7 +179,7 @@ impl OsString { #[inline] #[stable(feature = "os_str_bytes", since = "1.74.0")] pub unsafe fn from_encoded_bytes_unchecked(bytes: Vec) -> Self { - OsString { inner: Buf::from_encoded_bytes_unchecked(bytes) } + OsString { inner: unsafe { Buf::from_encoded_bytes_unchecked(bytes) } } } /// Converts to an [`OsStr`] slice. @@ -533,10 +528,39 @@ impl OsString { unsafe { Box::from_raw(rw) } } - /// Part of a hack to make PathBuf::push/pop more efficient. + /// Consumes and leaks the `OsString`, returning a mutable reference to the contents, + /// `&'a mut OsStr`. + /// + /// The caller has free choice over the returned lifetime, including 'static. + /// Indeed, this function is ideally used for data that lives for the remainder of + /// the program’s life, as dropping the returned reference will cause a memory leak. + /// + /// It does not reallocate or shrink the `OsString`, so the leaked allocation may include + /// unused capacity that is not part of the returned slice. If you want to discard excess + /// capacity, call [`into_boxed_os_str`], and then [`Box::leak`] instead. + /// However, keep in mind that trimming the capacity may result in a reallocation and copy. + /// + /// [`into_boxed_os_str`]: Self::into_boxed_os_str + #[unstable(feature = "os_string_pathbuf_leak", issue = "125965")] + #[inline] + pub fn leak<'a>(self) -> &'a mut OsStr { + OsStr::from_inner_mut(self.inner.leak()) + } + + /// Provides plumbing to core `Vec::truncate`. + /// More well behaving alternative to allowing outer types + /// full mutable access to the core `Vec`. + #[inline] + pub(crate) fn truncate(&mut self, len: usize) { + self.inner.truncate(len); + } + + /// Provides plumbing to core `Vec::extend_from_slice`. + /// More well behaving alternative to allowing outer types + /// full mutable access to the core `Vec`. #[inline] - pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec { - self.inner.as_mut_vec_for_path_buf() + pub(crate) fn extend_from_slice(&mut self, other: &[u8]) { + self.inner.extend_from_slice(other); } } @@ -784,7 +808,7 @@ impl OsStr { #[inline] #[stable(feature = "os_str_bytes", since = "1.74.0")] pub unsafe fn from_encoded_bytes_unchecked(bytes: &[u8]) -> &Self { - Self::from_inner(Slice::from_encoded_bytes_unchecked(bytes)) + Self::from_inner(unsafe { Slice::from_encoded_bytes_unchecked(bytes) }) } #[inline] diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs index b020e05eaab20..5b39b9e34d8c7 100644 --- a/library/std/src/ffi/os_str/tests.rs +++ b/library/std/src/ffi/os_str/tests.rs @@ -23,6 +23,15 @@ fn test_os_string_clear() { assert_eq!(0, os_string.inner.as_inner().len()); } +#[test] +fn test_os_string_leak() { + let os_string = OsString::from("have a cake"); + let (len, cap) = (os_string.len(), os_string.capacity()); + let leaked = os_string.leak(); + assert_eq!(leaked.as_encoded_bytes(), b"have a cake"); + unsafe { drop(String::from_raw_parts(leaked as *mut OsStr as _, len, cap)) } +} + #[test] fn test_os_string_capacity() { let os_string = OsString::with_capacity(0); diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 77e94365b08ec..c5edb03bb08be 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -50,7 +50,7 @@ use crate::time::SystemTime; /// } /// ``` /// -/// Read the contents of a file into a [`String`] (you can also use [`read`]): +/// Reads the contents of a file into a [`String`] (you can also use [`read`]): /// /// ```no_run /// use std::fs::File; @@ -229,7 +229,7 @@ pub struct DirBuilder { recursive: bool, } -/// Read the entire contents of a file into a bytes vector. +/// Reads the entire contents of a file into a bytes vector. /// /// This is a convenience function for using [`File::open`] and [`read_to_end`] /// with fewer imports and without an intermediate variable. @@ -268,7 +268,7 @@ pub fn read>(path: P) -> io::Result> { inner(path.as_ref()) } -/// Read the entire contents of a file into a string. +/// Reads the entire contents of a file into a string. /// /// This is a convenience function for using [`File::open`] and [`read_to_string`] /// with fewer imports and without an intermediate variable. @@ -311,7 +311,7 @@ pub fn read_to_string>(path: P) -> io::Result { inner(path.as_ref()) } -/// Write a slice as the entire contents of a file. +/// Writes a slice as the entire contents of a file. /// /// This function will create a file if it does not exist, /// and will entirely replace its contents if it does. @@ -767,11 +767,33 @@ fn buffer_capacity_required(mut file: &File) -> Option { #[stable(feature = "rust1", since = "1.0.0")] impl Read for &File { + /// Reads some bytes from the file. + /// + /// See [`Read::read`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `read` function on Unix and + /// the `NtReadFile` function on Windows. Note that this [may change in + /// the future][changes]. + /// + /// [changes]: io#platform-specific-behavior #[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result { self.inner.read(buf) } + /// Like `read`, except that it reads into a slice of buffers. + /// + /// See [`Read::read_vectored`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `readv` function on Unix and + /// falls back to the `read` implementation on Windows. Note that this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior #[inline] fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { self.inner.read_vectored(bufs) @@ -782,6 +804,16 @@ impl Read for &File { self.inner.read_buf(cursor) } + /// Determines if `File` has an efficient `read_vectored` implementation. + /// + /// See [`Read::is_read_vectored`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently returns `true` on Unix an `false` on Windows. + /// Note that this [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior #[inline] fn is_read_vectored(&self) -> bool { self.inner.is_read_vectored() @@ -803,19 +835,63 @@ impl Read for &File { } #[stable(feature = "rust1", since = "1.0.0")] impl Write for &File { + /// Writes some bytes from the file. + /// + /// See [`Write::write`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `write` function on Unix and + /// the `NtWriteFile` function on Windows. Note that this [may change in + /// the future][changes]. + /// + /// [changes]: io#platform-specific-behavior fn write(&mut self, buf: &[u8]) -> io::Result { self.inner.write(buf) } + /// Like `write`, except that it writes into a slice of buffers. + /// + /// See [`Write::write_vectored`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `writev` function on Unix + /// and falls back to the `write` implementation on Windows. Note that this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.inner.write_vectored(bufs) } + /// Determines if `File` has an efficient `write_vectored` implementation. + /// + /// See [`Write::is_write_vectored`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// This function currently returns `true` on Unix an `false` on Windows. + /// Note that this [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior #[inline] fn is_write_vectored(&self) -> bool { self.inner.is_write_vectored() } + /// Flushes the file, ensuring that all intermediately buffered contents + /// reach their destination. + /// + /// See [`Write::flush`] docs for more info. + /// + /// # Platform-specific behavior + /// + /// Since a `File` structure doesn't contain any buffers, this function is + /// currently a no-op on Unix and Windows. Note that this [may change in + /// the future][changes]. + /// + /// [changes]: io#platform-specific-behavior #[inline] fn flush(&mut self) -> io::Result<()> { self.inner.flush() @@ -1450,7 +1526,7 @@ impl FromInner for Metadata { } impl FileTimes { - /// Create a new `FileTimes` with no times set. + /// Creates a new `FileTimes` with no times set. /// /// Using the resulting `FileTimes` in [`File::set_times`] will not modify any timestamps. #[stable(feature = "file_set_times", since = "1.75.0")] @@ -1929,7 +2005,7 @@ pub fn remove_file>(path: P) -> io::Result<()> { fs_imp::unlink(path.as_ref()) } -/// Given a path, query the file system to get information about a file, +/// Given a path, queries the file system to get information about a file, /// directory, etc. /// /// This function will traverse symbolic links to query information about the @@ -1968,7 +2044,7 @@ pub fn metadata>(path: P) -> io::Result { fs_imp::stat(path.as_ref()).map(Metadata) } -/// Query the metadata about a file without following symlinks. +/// Queries the metadata about a file without following symlinks. /// /// # Platform-specific behavior /// @@ -2003,7 +2079,7 @@ pub fn symlink_metadata>(path: P) -> io::Result { fs_imp::lstat(path.as_ref()).map(Metadata) } -/// Rename a file or directory to a new name, replacing the original file if +/// Renames a file or directory to a new name, replacing the original file if /// `to` already exists. /// /// This will not work if the new name is on a different mount point. @@ -2226,7 +2302,7 @@ pub fn read_link>(path: P) -> io::Result { /// /// This function currently corresponds to the `realpath` function on Unix /// and the `CreateFile` and `GetFinalPathNameByHandle` functions on Windows. -/// Note that, this [may change in the future][changes]. +/// Note that this [may change in the future][changes]. /// /// On Windows, this converts the path to use [extended length path][path] /// syntax, which allows your program to use longer path names, but means you @@ -2310,6 +2386,9 @@ pub fn create_dir>(path: P) -> io::Result<()> { /// If this function returns an error, some of the parent components might have /// been created already. /// +/// If the empty path is passed to this function, it always succeeds without +/// creating any directories. +/// /// # Platform-specific behavior /// /// This function currently corresponds to multiple calls to the `mkdir` @@ -2321,13 +2400,8 @@ pub fn create_dir>(path: P) -> io::Result<()> { /// /// # Errors /// -/// This function will return an error in the following situations, but is not -/// limited to just these cases: -/// -/// * If any directory in the path specified by `path` -/// does not already exist and it could not be created otherwise. The specific -/// error conditions for when a directory is being created (after it is -/// determined to not exist) are outlined by [`fs::create_dir`]. +/// The function will return an error if any directory specified in path does not exist and +/// could not be created. There may be other error conditions; see [`fs::create_dir`] for specifics. /// /// Notable exception is made for situations where any of the directories /// specified in the `path` could not be created as it was being created concurrently. @@ -2663,18 +2737,15 @@ impl AsInnerMut for DirBuilder { /// # Examples /// /// ```no_run -/// #![feature(fs_try_exists)] /// use std::fs; /// -/// assert!(!fs::try_exists("does_not_exist.txt").expect("Can't check existence of file does_not_exist.txt")); -/// assert!(fs::try_exists("/root/secret_file.txt").is_err()); +/// assert!(!fs::exists("does_not_exist.txt").expect("Can't check existence of file does_not_exist.txt")); +/// assert!(fs::exists("/root/secret_file.txt").is_err()); /// ``` /// /// [`Path::exists`]: crate::path::Path::exists -// FIXME: stabilization should modify documentation of `exists()` to recommend this method -// instead. -#[unstable(feature = "fs_try_exists", issue = "83186")] +#[stable(feature = "fs_try_exists", since = "1.81.0")] #[inline] -pub fn try_exists>(path: P) -> io::Result { - fs_imp::try_exists(path.as_ref()) +pub fn exists>(path: P) -> io::Result { + fs_imp::exists(path.as_ref()) } diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index dfa05671ab0f1..13028c4c3b57e 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1,20 +1,11 @@ -use crate::io::prelude::*; - -use crate::env; -use crate::fs::{self, File, FileTimes, OpenOptions}; -use crate::io::{BorrowedBuf, ErrorKind, SeekFrom}; -use crate::mem::MaybeUninit; -use crate::path::Path; -use crate::str; -use crate::sync::Arc; -use crate::sys_common::io::test::{tmpdir, TempDir}; -use crate::thread; -use crate::time::{Duration, Instant, SystemTime}; - use rand::RngCore; #[cfg(target_os = "macos")] use crate::ffi::{c_char, c_int}; +use crate::fs::{self, File, FileTimes, OpenOptions}; +use crate::io::prelude::*; +use crate::io::{BorrowedBuf, ErrorKind, SeekFrom}; +use crate::mem::MaybeUninit; #[cfg(unix)] use crate::os::unix::fs::symlink as symlink_dir; #[cfg(unix)] @@ -23,8 +14,13 @@ use crate::os::unix::fs::symlink as symlink_file; use crate::os::unix::fs::symlink as junction_point; #[cfg(windows)] use crate::os::windows::fs::{junction_point, symlink_dir, symlink_file, OpenOptionsExt}; +use crate::path::Path; +use crate::sync::Arc; #[cfg(target_os = "macos")] use crate::sys::weak::weak; +use crate::sys_common::io::test::{tmpdir, TempDir}; +use crate::time::{Duration, Instant, SystemTime}; +use crate::{env, str, thread}; macro_rules! check { ($e:expr) => { @@ -406,7 +402,7 @@ fn file_test_read_buf() { let filename = &tmpdir.join("test"); check!(fs::write(filename, &[1, 2, 3, 4])); - let mut buf: [MaybeUninit; 128] = MaybeUninit::uninit_array(); + let mut buf: [MaybeUninit; 128] = [MaybeUninit::uninit(); 128]; let mut buf = BorrowedBuf::from(buf.as_mut_slice()); let mut file = check!(File::open(filename)); check!(file.read_buf(buf.unfilled())); @@ -683,7 +679,7 @@ fn recursive_rmdir_toctou() { let drop_canary_arc = Arc::new(()); let drop_canary_weak = Arc::downgrade(&drop_canary_arc); - eprintln!("x: {:?}", &victim_del_path); + eprintln!("x: {victim_del_path:?}"); // victim just continuously removes `victim_del` thread::spawn(move || { @@ -1431,7 +1427,7 @@ fn metadata_access_times() { assert_eq!(check!(a.modified()), check!(a.modified())); assert_eq!(check!(b.accessed()), check!(b.modified())); - if cfg!(target_os = "macos") || cfg!(target_os = "windows") { + if cfg!(target_vendor = "apple") || cfg!(target_os = "windows") { check!(a.created()); check!(b.created()); } @@ -1514,7 +1510,9 @@ fn symlink_hard_link() { #[test] #[cfg(windows)] fn create_dir_long_paths() { - use crate::{ffi::OsStr, iter, os::windows::ffi::OsStrExt}; + use crate::ffi::OsStr; + use crate::iter; + use crate::os::windows::ffi::OsStrExt; const PATH_LEN: usize = 247; let tmpdir = tmpdir(); @@ -1638,16 +1636,8 @@ fn rename_directory() { #[test] fn test_file_times() { - #[cfg(target_os = "ios")] - use crate::os::ios::fs::FileTimesExt; - #[cfg(target_os = "macos")] - use crate::os::macos::fs::FileTimesExt; - #[cfg(target_os = "tvos")] - use crate::os::tvos::fs::FileTimesExt; - #[cfg(target_os = "visionos")] - use crate::os::visionos::fs::FileTimesExt; - #[cfg(target_os = "watchos")] - use crate::os::watchos::fs::FileTimesExt; + #[cfg(target_vendor = "apple")] + use crate::os::darwin::fs::FileTimesExt; #[cfg(windows)] use crate::os::windows::fs::FileTimesExt; @@ -1693,16 +1683,7 @@ fn test_file_times() { #[test] #[cfg(target_vendor = "apple")] fn test_file_times_pre_epoch_with_nanos() { - #[cfg(target_os = "ios")] - use crate::os::ios::fs::FileTimesExt; - #[cfg(target_os = "macos")] - use crate::os::macos::fs::FileTimesExt; - #[cfg(target_os = "tvos")] - use crate::os::tvos::fs::FileTimesExt; - #[cfg(target_os = "visionos")] - use crate::os::visionos::fs::FileTimesExt; - #[cfg(target_os = "watchos")] - use crate::os::watchos::fs::FileTimesExt; + use crate::os::darwin::fs::FileTimesExt; let tmp = tmpdir(); let file = File::create(tmp.join("foo")).unwrap(); diff --git a/library/std/src/hash/random.rs b/library/std/src/hash/random.rs index a1ccbb25369bf..8ef45172eac40 100644 --- a/library/std/src/hash/random.rs +++ b/library/std/src/hash/random.rs @@ -6,11 +6,11 @@ //! outside this crate. //! //! [`collections`]: crate::collections + #[allow(deprecated)] use super::{BuildHasher, Hasher, SipHasher13}; use crate::cell::Cell; -use crate::fmt; -use crate::sys; +use crate::{fmt, sys}; /// `RandomState` is the default state for [`HashMap`] types. /// diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 0cdc49c87d8fc..f11dd50c5e2b7 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -1,11 +1,12 @@ mod buffer; +use buffer::Buffer; + use crate::fmt; use crate::io::{ self, uninlined_slow_read_byte, BorrowedCursor, BufRead, IoSliceMut, Read, Seek, SeekFrom, SizeHint, SpecReadByte, DEFAULT_BUF_SIZE, }; -use buffer::Buffer; /// The `BufReader` struct adds buffering to any reader. /// diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index e9e29d60ca282..796137c0123e7 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -1,13 +1,14 @@ -///! An encapsulation of `BufReader`'s buffer management logic. -/// -/// This module factors out the basic functionality of `BufReader` in order to protect two core -/// invariants: -/// * `filled` bytes of `buf` are always initialized -/// * `pos` is always <= `filled` -/// Since this module encapsulates the buffer management logic, we can ensure that the range -/// `pos..filled` is always a valid index into the initialized region of the buffer. This means -/// that user code which wants to do reads from a `BufReader` via `buffer` + `consume` can do so -/// without encountering any runtime bounds checks. +//! An encapsulation of `BufReader`'s buffer management logic. +//! +//! This module factors out the basic functionality of `BufReader` in order to protect two core +//! invariants: +//! * `filled` bytes of `buf` are always initialized +//! * `pos` is always <= `filled` +//! Since this module encapsulates the buffer management logic, we can ensure that the range +//! `pos..filled` is always a valid index into the initialized region of the buffer. This means +//! that user code which wants to do reads from a `BufReader` via `buffer` + `consume` can do so +//! without encountering any runtime bounds checks. + use crate::cmp; use crate::io::{self, BorrowedBuf, Read}; use crate::mem::MaybeUninit; diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 2d13230ffbabd..21650d467446e 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -1,10 +1,8 @@ -use crate::error; -use crate::fmt; use crate::io::{ self, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE, }; -use crate::mem; -use crate::ptr; +use crate::mem::{self, ManuallyDrop}; +use crate::{error, fmt, ptr}; /// Wraps a writer and buffers its output. /// @@ -164,13 +162,13 @@ impl BufWriter { /// assert_eq!(&buffered_data.unwrap(), b"ata"); /// ``` #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] - pub fn into_parts(mut self) -> (W, Result, WriterPanicked>) { - let buf = mem::take(&mut self.buf); - let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) }; + pub fn into_parts(self) -> (W, Result, WriterPanicked>) { + let mut this = ManuallyDrop::new(self); + let buf = mem::take(&mut this.buf); + let buf = if !this.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) }; - // SAFETY: forget(self) prevents double dropping inner - let inner = unsafe { ptr::read(&self.inner) }; - mem::forget(self); + // SAFETY: double-drops are prevented by putting `this` in a ManuallyDrop that is never dropped + let inner = unsafe { ptr::read(&this.inner) }; (inner, buf) } @@ -433,9 +431,11 @@ impl BufWriter { let old_len = self.buf.len(); let buf_len = buf.len(); let src = buf.as_ptr(); - let dst = self.buf.as_mut_ptr().add(old_len); - ptr::copy_nonoverlapping(src, dst, buf_len); - self.buf.set_len(old_len + buf_len); + unsafe { + let dst = self.buf.as_mut_ptr().add(old_len); + ptr::copy_nonoverlapping(src, dst, buf_len); + self.buf.set_len(old_len + buf_len); + } } #[inline] diff --git a/library/std/src/io/buffered/linewriter.rs b/library/std/src/io/buffered/linewriter.rs index 3d4ae70419322..cc6921b86dd0b 100644 --- a/library/std/src/io/buffered/linewriter.rs +++ b/library/std/src/io/buffered/linewriter.rs @@ -1,5 +1,6 @@ use crate::fmt; -use crate::io::{self, buffered::LineWriterShim, BufWriter, IntoInnerError, IoSlice, Write}; +use crate::io::buffered::LineWriterShim; +use crate::io::{self, BufWriter, IntoInnerError, IoSlice, Write}; /// Wraps a writer and buffers output to it, flushing whenever a newline /// (`0x0a`, `'\n'`) is detected. diff --git a/library/std/src/io/buffered/linewritershim.rs b/library/std/src/io/buffered/linewritershim.rs index c3ac7855d4450..3d04ccd1c7d81 100644 --- a/library/std/src/io/buffered/linewritershim.rs +++ b/library/std/src/io/buffered/linewritershim.rs @@ -1,7 +1,9 @@ -use crate::io::{self, BufWriter, IoSlice, Write}; use core::slice::memchr; +use crate::io::{self, BufWriter, IoSlice, Write}; + /// Private helper struct for implementing the line-buffered writing logic. +/// /// This shim temporarily wraps a BufWriter, and uses its internals to /// implement a line-buffered writer (specifically by using the internal /// methods like write_to_buf and flush_buf). In this way, a more @@ -20,27 +22,27 @@ impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> { Self { buffer } } - /// Get a reference to the inner writer (that is, the writer + /// Gets a reference to the inner writer (that is, the writer /// wrapped by the BufWriter). fn inner(&self) -> &W { self.buffer.get_ref() } - /// Get a mutable reference to the inner writer (that is, the writer + /// Gets a mutable reference to the inner writer (that is, the writer /// wrapped by the BufWriter). Be careful with this writer, as writes to /// it will bypass the buffer. fn inner_mut(&mut self) -> &mut W { self.buffer.get_mut() } - /// Get the content currently buffered in self.buffer + /// Gets the content currently buffered in self.buffer fn buffered(&self) -> &[u8] { self.buffer.buffer() } - /// Flush the buffer iff the last byte is a newline (indicating that an + /// Flushes the buffer iff the last byte is a newline (indicating that an /// earlier write only succeeded partially, and we want to retry flushing - /// the buffered line before continuing with a subsequent write) + /// the buffered line before continuing with a subsequent write). fn flush_if_completed_line(&mut self) -> io::Result<()> { match self.buffered().last().copied() { Some(b'\n') => self.buffer.flush_buf(), @@ -50,10 +52,11 @@ impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> { } impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { - /// Write some data into this BufReader with line buffering. This means - /// that, if any newlines are present in the data, the data up to the last - /// newline is sent directly to the underlying writer, and data after it - /// is buffered. Returns the number of bytes written. + /// Writes some data into this BufReader with line buffering. + /// + /// This means that, if any newlines are present in the data, the data up to + /// the last newline is sent directly to the underlying writer, and data + /// after it is buffered. Returns the number of bytes written. /// /// This function operates on a "best effort basis"; in keeping with the /// convention of `Write::write`, it makes at most one attempt to write @@ -136,11 +139,12 @@ impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { self.buffer.flush() } - /// Write some vectored data into this BufReader with line buffering. This - /// means that, if any newlines are present in the data, the data up to - /// and including the buffer containing the last newline is sent directly - /// to the inner writer, and the data after it is buffered. Returns the - /// number of bytes written. + /// Writes some vectored data into this BufReader with line buffering. + /// + /// This means that, if any newlines are present in the data, the data up to + /// and including the buffer containing the last newline is sent directly to + /// the inner writer, and the data after it is buffered. Returns the number + /// of bytes written. /// /// This function operates on a "best effort basis"; in keeping with the /// convention of `Write::write`, it makes at most one attempt to write @@ -245,10 +249,11 @@ impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { self.inner().is_write_vectored() } - /// Write some data into this BufReader with line buffering. This means - /// that, if any newlines are present in the data, the data up to the last - /// newline is sent directly to the underlying writer, and data after it - /// is buffered. + /// Writes some data into this BufReader with line buffering. + /// + /// This means that, if any newlines are present in the data, the data up to + /// the last newline is sent directly to the underlying writer, and data + /// after it is buffered. /// /// Because this function attempts to send completed lines to the underlying /// writer, it will also flush the existing buffer if it contains any diff --git a/library/std/src/io/buffered/mod.rs b/library/std/src/io/buffered/mod.rs index 100dab1e2493c..475d877528f7f 100644 --- a/library/std/src/io/buffered/mod.rs +++ b/library/std/src/io/buffered/mod.rs @@ -8,16 +8,14 @@ mod linewritershim; #[cfg(test)] mod tests; -use crate::error; -use crate::fmt; -use crate::io::Error; +#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] +pub use bufwriter::WriterPanicked; +use linewritershim::LineWriterShim; #[stable(feature = "rust1", since = "1.0.0")] pub use self::{bufreader::BufReader, bufwriter::BufWriter, linewriter::LineWriter}; -use linewritershim::LineWriterShim; - -#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] -pub use bufwriter::WriterPanicked; +use crate::io::Error; +use crate::{error, fmt}; /// An error returned by [`BufWriter::into_inner`] which combines an error that /// happened while writing out the buffer, and the buffered writer object @@ -48,7 +46,7 @@ pub use bufwriter::WriterPanicked; pub struct IntoInnerError(W, Error); impl IntoInnerError { - /// Construct a new IntoInnerError + /// Constructs a new IntoInnerError fn new(writer: W, error: Error) -> Self { Self(writer, error) } diff --git a/library/std/src/io/buffered/tests.rs b/library/std/src/io/buffered/tests.rs index ee0db30e22c2e..d89ecd317d6ee 100644 --- a/library/std/src/io/buffered/tests.rs +++ b/library/std/src/io/buffered/tests.rs @@ -3,9 +3,8 @@ use crate::io::{ self, BorrowedBuf, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, SeekFrom, }; use crate::mem::MaybeUninit; -use crate::panic; use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::thread; +use crate::{panic, thread}; /// A dummy reader intended at testing short-reads propagation. pub struct ShortReader { @@ -1067,3 +1066,13 @@ fn bufreader_full_initialize() { // But we initialized the whole buffer! assert_eq!(reader.initialized(), reader.capacity()); } + +/// This is a regression test for https://github.com/rust-lang/rust/issues/127584. +#[test] +fn bufwriter_aliasing() { + use crate::io::{BufWriter, Cursor}; + let mut v = vec![0; 1024]; + let c = Cursor::new(&mut v); + let w = BufWriter::new(Box::new(c)); + let _ = w.into_parts(); +} diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs index a1f909a3c5386..7e08826a7e1d8 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/std/src/io/copy/tests.rs @@ -119,13 +119,12 @@ fn copy_specializes_from_slice() { #[cfg(unix)] mod io_benches { - use crate::fs::File; - use crate::fs::OpenOptions; + use test::Bencher; + + use crate::fs::{File, OpenOptions}; use crate::io::prelude::*; use crate::io::BufReader; - use test::Bencher; - #[bench] fn bench_copy_buf_reader(b: &mut Bencher) { let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed"); diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index a1a8b2a3505c7..9f913eae09544 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -1,10 +1,9 @@ #[cfg(test)] mod tests; -use crate::io::prelude::*; - use crate::alloc::Allocator; use crate::cmp; +use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; /// A `Cursor` wraps an in-memory buffer and provides it with a @@ -210,55 +209,60 @@ impl Cursor where T: AsRef<[u8]>, { - /// Returns the remaining slice. + /// Splits the underlying slice at the cursor position and returns them. /// /// # Examples /// /// ``` - /// #![feature(cursor_remaining)] + /// #![feature(cursor_split)] /// use std::io::Cursor; /// /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); /// - /// assert_eq!(buff.remaining_slice(), &[1, 2, 3, 4, 5]); + /// assert_eq!(buff.split(), ([].as_slice(), [1, 2, 3, 4, 5].as_slice())); /// /// buff.set_position(2); - /// assert_eq!(buff.remaining_slice(), &[3, 4, 5]); - /// - /// buff.set_position(4); - /// assert_eq!(buff.remaining_slice(), &[5]); + /// assert_eq!(buff.split(), ([1, 2].as_slice(), [3, 4, 5].as_slice())); /// /// buff.set_position(6); - /// assert_eq!(buff.remaining_slice(), &[]); + /// assert_eq!(buff.split(), ([1, 2, 3, 4, 5].as_slice(), [].as_slice())); /// ``` - #[unstable(feature = "cursor_remaining", issue = "86369")] - pub fn remaining_slice(&self) -> &[u8] { - let len = self.pos.min(self.inner.as_ref().len() as u64); - &self.inner.as_ref()[(len as usize)..] + #[unstable(feature = "cursor_split", issue = "86369")] + pub fn split(&self) -> (&[u8], &[u8]) { + let slice = self.inner.as_ref(); + let pos = self.pos.min(slice.len() as u64); + slice.split_at(pos as usize) } +} - /// Returns `true` if the remaining slice is empty. +impl Cursor +where + T: AsMut<[u8]>, +{ + /// Splits the underlying slice at the cursor position and returns them + /// mutably. /// /// # Examples /// /// ``` - /// #![feature(cursor_remaining)] + /// #![feature(cursor_split)] /// use std::io::Cursor; /// /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); /// - /// buff.set_position(2); - /// assert!(!buff.is_empty()); + /// assert_eq!(buff.split_mut(), ([].as_mut_slice(), [1, 2, 3, 4, 5].as_mut_slice())); /// - /// buff.set_position(5); - /// assert!(buff.is_empty()); + /// buff.set_position(2); + /// assert_eq!(buff.split_mut(), ([1, 2].as_mut_slice(), [3, 4, 5].as_mut_slice())); /// - /// buff.set_position(10); - /// assert!(buff.is_empty()); + /// buff.set_position(6); + /// assert_eq!(buff.split_mut(), ([1, 2, 3, 4, 5].as_mut_slice(), [].as_mut_slice())); /// ``` - #[unstable(feature = "cursor_remaining", issue = "86369")] - pub fn is_empty(&self) -> bool { - self.pos >= self.inner.as_ref().len() as u64 + #[unstable(feature = "cursor_split", issue = "86369")] + pub fn split_mut(&mut self) -> (&mut [u8], &mut [u8]) { + let slice = self.inner.as_mut(); + let pos = self.pos.min(slice.len() as u64); + slice.split_at_mut(pos as usize) } } @@ -320,7 +324,7 @@ where T: AsRef<[u8]>, { fn read(&mut self, buf: &mut [u8]) -> io::Result { - let n = Read::read(&mut self.remaining_slice(), buf)?; + let n = Read::read(&mut Cursor::split(self).1, buf)?; self.pos += n as u64; Ok(n) } @@ -328,7 +332,7 @@ where fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { let prev_written = cursor.written(); - Read::read_buf(&mut self.remaining_slice(), cursor.reborrow())?; + Read::read_buf(&mut Cursor::split(self).1, cursor.reborrow())?; self.pos += (cursor.written() - prev_written) as u64; @@ -352,7 +356,7 @@ where } fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - let result = Read::read_exact(&mut self.remaining_slice(), buf); + let result = Read::read_exact(&mut Cursor::split(self).1, buf); match result { Ok(_) => self.pos += buf.len() as u64, @@ -366,14 +370,14 @@ where fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { let prev_written = cursor.written(); - let result = Read::read_buf_exact(&mut self.remaining_slice(), cursor.reborrow()); + let result = Read::read_buf_exact(&mut Cursor::split(self).1, cursor.reborrow()); self.pos += (cursor.written() - prev_written) as u64; result } fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - let content = self.remaining_slice(); + let content = Cursor::split(self).1; let len = content.len(); buf.try_reserve(len)?; buf.extend_from_slice(content); @@ -384,7 +388,7 @@ where fn read_to_string(&mut self, buf: &mut String) -> io::Result { let content = - crate::str::from_utf8(self.remaining_slice()).map_err(|_| io::Error::INVALID_UTF8)?; + crate::str::from_utf8(Cursor::split(self).1).map_err(|_| io::Error::INVALID_UTF8)?; let len = content.len(); buf.try_reserve(len)?; buf.push_str(content); @@ -400,7 +404,7 @@ where T: AsRef<[u8]>, { fn fill_buf(&mut self) -> io::Result<&[u8]> { - Ok(self.remaining_slice()) + Ok(Cursor::split(self).1) } fn consume(&mut self, amt: usize) { self.pos += amt as u64; @@ -482,7 +486,7 @@ where A: Allocator, { debug_assert!(vec.capacity() >= pos + buf.len()); - vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()); + unsafe { vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()) }; pos + buf.len() } diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index f366cb8f42baa..e8ae1d99fbf37 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -11,10 +11,7 @@ mod repr_unpacked; #[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] use repr_unpacked::Repr; -use crate::error; -use crate::fmt; -use crate::result; -use crate::sys; +use crate::{error, fmt, result, sys}; /// A specialized [`Result`] type for I/O operations. /// @@ -167,7 +164,7 @@ impl SimpleMessage { } } -/// Create and return an `io::Error` for a given `ErrorKind` and constant +/// Creates and returns an `io::Error` for a given `ErrorKind` and constant /// message. This doesn't allocate. pub(crate) macro const_io_error($kind:expr, $message:expr $(,)?) { $crate::io::error::Error::from_static_message({ @@ -775,7 +772,7 @@ impl Error { /// /// impl Display for MyError { /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "MyError: {}", &self.v) + /// write!(f, "MyError: {}", self.v) /// } /// } /// @@ -852,7 +849,7 @@ impl Error { } } - /// Attempt to downcast the custom boxed error to `E`. + /// Attempts to downcast the custom boxed error to `E`. /// /// If this [`Error`] contains a custom boxed error, /// then it would attempt downcasting on the boxed error, diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index 6f8d5e3777568..9d3ade46bd929 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -28,7 +28,7 @@ //! //! # Layout //! Tagged values are 64 bits, with the 2 least significant bits used for the -//! tag. This means there are there are 4 "variants": +//! tag. This means there are 4 "variants": //! //! - **Tag 0b00**: The first variant is equivalent to //! `ErrorData::SimpleMessage`, and holds a `&'static SimpleMessage` directly. @@ -102,11 +102,11 @@ //! to use a pointer type to store something that may hold an integer, some of //! the time. -use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; use core::marker::PhantomData; -use core::mem::{align_of, size_of}; use core::ptr::{self, NonNull}; +use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; + // The 2 least-significant bits are used as tag. const TAG_MASK: usize = 0b11; const TAG_SIMPLE_MESSAGE: usize = 0b00; @@ -268,11 +268,14 @@ where // Using this rather than unwrap meaningfully improves the code // for callers which only care about one variant (usually // `Custom`) - core::hint::unreachable_unchecked(); + unsafe { core::hint::unreachable_unchecked() }; }); ErrorData::Simple(kind) } - TAG_SIMPLE_MESSAGE => ErrorData::SimpleMessage(&*ptr.cast::().as_ptr()), + TAG_SIMPLE_MESSAGE => { + // SAFETY: per tag + unsafe { ErrorData::SimpleMessage(&*ptr.cast::().as_ptr()) } + } TAG_CUSTOM => { // It would be correct for us to use `ptr::byte_sub` here (see the // comment above the `wrapping_add` call in `new_custom` for why), diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index fc6db2825e811..064e2e36b7a1d 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,10 +1,9 @@ use super::{const_io_error, Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage}; use crate::assert_matches::assert_matches; -use crate::error; -use crate::fmt; use crate::mem::size_of; use crate::sys::decode_error_kind; use crate::sys::os::error_string; +use crate::{error, fmt}; #[test] fn test_size() { @@ -95,7 +94,8 @@ fn test_errorkind_packing() { #[test] fn test_simple_message_packing() { - use super::{ErrorKind::*, SimpleMessage}; + use super::ErrorKind::*; + use super::SimpleMessage; macro_rules! check_simple_msg { ($err:expr, $kind:ident, $msg:literal) => {{ let e = &$err; diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index a8a2e9413e11c..85023540a816f 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -2,12 +2,9 @@ mod tests; use crate::alloc::Allocator; -use crate::cmp; use crate::collections::VecDeque; -use crate::fmt; use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; -use crate::mem; -use crate::str; +use crate::{cmp, fmt, mem, str}; // ============================================================================= // Forwarding implementations diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index f55ec1588f91d..644b294db8da1 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -297,15 +297,12 @@ #[cfg(test)] mod tests; -use crate::cmp; -use crate::fmt; -use crate::mem::take; -use crate::ops::{Deref, DerefMut}; -use crate::slice; -use crate::str; -use crate::sys; +#[unstable(feature = "read_buf", issue = "78485")] +pub use core::io::{BorrowedBuf, BorrowedCursor}; use core::slice::memchr; +pub(crate) use error::const_io_error; + #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; #[unstable(feature = "raw_os_error_ty", issue = "107792")] @@ -328,10 +325,9 @@ pub use self::{ stdio::{stderr, stdin, stdout, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock}, util::{empty, repeat, sink, Empty, Repeat, Sink}, }; - -#[unstable(feature = "read_buf", issue = "78485")] -pub use core::io::{BorrowedBuf, BorrowedCursor}; -pub(crate) use error::const_io_error; +use crate::mem::take; +use crate::ops::{Deref, DerefMut}; +use crate::{cmp, fmt, slice, str, sys}; mod buffered; pub(crate) mod copy; @@ -382,11 +378,11 @@ pub(crate) unsafe fn append_to_string(buf: &mut String, f: F) -> Result) -> Result, { - let mut g = Guard { len: buf.len(), buf: buf.as_mut_vec() }; + let mut g = Guard { len: buf.len(), buf: unsafe { buf.as_mut_vec() } }; let ret = f(g.buf); // SAFETY: the caller promises to only append data to `buf` - let appended = g.buf.get_unchecked(g.len..); + let appended = unsafe { g.buf.get_unchecked(g.len..) }; if str::from_utf8(appended).is_err() { ret.and_then(|_| Err(Error::INVALID_UTF8)) } else { @@ -782,7 +778,7 @@ pub trait Read { false } - /// Read all bytes until EOF in this source, placing them into `buf`. + /// Reads all bytes until EOF in this source, placing them into `buf`. /// /// All bytes read from this source will be appended to the specified buffer /// `buf`. This function will continuously call [`read()`] to append more data to @@ -866,7 +862,7 @@ pub trait Read { default_read_to_end(self, buf, None) } - /// Read all bytes until EOF in this source, appending them to `buf`. + /// Reads all bytes until EOF in this source, appending them to `buf`. /// /// If successful, this function returns the number of bytes which were read /// and appended to `buf`. @@ -909,7 +905,7 @@ pub trait Read { default_read_to_string(self, buf, None) } - /// Read the exact number of bytes required to fill `buf`. + /// Reads the exact number of bytes required to fill `buf`. /// /// This function reads as many bytes as necessary to completely fill the /// specified buffer `buf`. @@ -973,7 +969,7 @@ pub trait Read { default_read_buf(|b| self.read(b), buf) } - /// Read the exact number of bytes required to fill `cursor`. + /// Reads the exact number of bytes required to fill `cursor`. /// /// This is similar to the [`read_exact`](Read::read_exact) method, except /// that it is passed a [`BorrowedCursor`] rather than `[u8]` to allow use @@ -1159,7 +1155,7 @@ pub trait Read { } } -/// Read all bytes from a [reader][Read] into a new [`String`]. +/// Reads all bytes from a [reader][Read] into a new [`String`]. /// /// This is a convenience function for [`Read::read_to_string`]. Using this /// function avoids having to create a variable first and provides more type @@ -1212,7 +1208,7 @@ pub fn read_to_string(mut reader: R) -> Result { /// A buffer type used with `Read::read_vectored`. /// -/// It is semantically a wrapper around an `&mut [u8]`, but is guaranteed to be +/// It is semantically a wrapper around a `&mut [u8]`, but is guaranteed to be /// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on /// Windows. #[stable(feature = "iovec", since = "1.36.0")] @@ -1256,8 +1252,6 @@ impl<'a> IoSliceMut<'a> { /// # Examples /// /// ``` - /// #![feature(io_slice_advance)] - /// /// use std::io::IoSliceMut; /// use std::ops::Deref; /// @@ -1268,7 +1262,7 @@ impl<'a> IoSliceMut<'a> { /// buf.advance(3); /// assert_eq!(buf.deref(), [1; 5].as_ref()); /// ``` - #[unstable(feature = "io_slice_advance", issue = "62726")] + #[stable(feature = "io_slice_advance", since = "1.81.0")] #[inline] pub fn advance(&mut self, n: usize) { self.0.advance(n) @@ -1290,8 +1284,6 @@ impl<'a> IoSliceMut<'a> { /// # Examples /// /// ``` - /// #![feature(io_slice_advance)] - /// /// use std::io::IoSliceMut; /// use std::ops::Deref; /// @@ -1309,7 +1301,7 @@ impl<'a> IoSliceMut<'a> { /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); /// ``` - #[unstable(feature = "io_slice_advance", issue = "62726")] + #[stable(feature = "io_slice_advance", since = "1.81.0")] #[inline] pub fn advance_slices(bufs: &mut &mut [IoSliceMut<'a>], n: usize) { // Number of buffers to remove. @@ -1400,8 +1392,6 @@ impl<'a> IoSlice<'a> { /// # Examples /// /// ``` - /// #![feature(io_slice_advance)] - /// /// use std::io::IoSlice; /// use std::ops::Deref; /// @@ -1412,7 +1402,7 @@ impl<'a> IoSlice<'a> { /// buf.advance(3); /// assert_eq!(buf.deref(), [1; 5].as_ref()); /// ``` - #[unstable(feature = "io_slice_advance", issue = "62726")] + #[stable(feature = "io_slice_advance", since = "1.81.0")] #[inline] pub fn advance(&mut self, n: usize) { self.0.advance(n) @@ -1434,8 +1424,6 @@ impl<'a> IoSlice<'a> { /// # Examples /// /// ``` - /// #![feature(io_slice_advance)] - /// /// use std::io::IoSlice; /// use std::ops::Deref; /// @@ -1452,7 +1440,7 @@ impl<'a> IoSlice<'a> { /// IoSlice::advance_slices(&mut bufs, 10); /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - #[unstable(feature = "io_slice_advance", issue = "62726")] + #[stable(feature = "io_slice_advance", since = "1.81.0")] #[inline] pub fn advance_slices(bufs: &mut &mut [IoSlice<'a>], n: usize) { // Number of buffers to remove. @@ -1539,7 +1527,7 @@ impl<'a> Deref for IoSlice<'a> { #[doc(notable_trait)] #[cfg_attr(not(test), rustc_diagnostic_item = "IoWrite")] pub trait Write { - /// Write a buffer into this writer, returning how many bytes were written. + /// Writes a buffer into this writer, returning how many bytes were written. /// /// This function will attempt to write the entire contents of `buf`, but /// the entire write might not succeed, or the write may also generate an @@ -1638,7 +1626,7 @@ pub trait Write { false } - /// Flush this output stream, ensuring that all intermediately buffered + /// Flushes this output stream, ensuring that all intermediately buffered /// contents reach their destination. /// /// # Errors @@ -2058,7 +2046,7 @@ pub trait Seek { /// ``` /// /// [`BufReader`]: crate::io::BufReader - #[stable(feature = "seek_seek_relative", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "seek_seek_relative", since = "1.80.0")] fn seek_relative(&mut self, offset: i64) -> Result<()> { self.seek(SeekFrom::Current(offset))?; Ok(()) @@ -2255,7 +2243,7 @@ pub trait BufRead: Read { #[stable(feature = "rust1", since = "1.0.0")] fn consume(&mut self, amt: usize); - /// Check if the underlying `Read` has any data left to be read. + /// Checks if the underlying `Read` has any data left to be read. /// /// This function may fill the buffer to check for data, /// so this functions returns `Result`, not `bool`. @@ -2286,7 +2274,7 @@ pub trait BufRead: Read { self.fill_buf().map(|b| !b.is_empty()) } - /// Read all bytes into `buf` until the delimiter `byte` or EOF is reached. + /// Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. /// /// This function will read bytes from the underlying stream until the /// delimiter or EOF is found. Once found, all bytes up to, and including, @@ -2345,7 +2333,7 @@ pub trait BufRead: Read { read_until(self, byte, buf) } - /// Skip all bytes until the delimiter `byte` or EOF is reached. + /// Skips all bytes until the delimiter `byte` or EOF is reached. /// /// This function will read (and discard) bytes from the underlying stream until the /// delimiter or EOF is found. @@ -2407,7 +2395,7 @@ pub trait BufRead: Read { skip_until(self, byte) } - /// Read all bytes until a newline (the `0xA` byte) is reached, and append + /// Reads all bytes until a newline (the `0xA` byte) is reached, and append /// them to the provided `String` buffer. /// /// Previous content of the buffer will be preserved. To avoid appending to @@ -3046,7 +3034,7 @@ where } } -/// Read a single byte in a slow, generic way. This is used by the default +/// Reads a single byte in a slow, generic way. This is used by the default /// `spec_read_byte`. #[inline] fn inlined_slow_read_byte(reader: &mut R) -> Option> { diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index c8968b74b12d1..6de069a518e3d 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -3,11 +3,10 @@ #[cfg(test)] mod tests; -use crate::io::prelude::*; - use crate::cell::{Cell, RefCell}; use crate::fmt; use crate::fs::File; +use crate::io::prelude::*; use crate::io::{ self, BorrowedCursor, BufReader, IoSlice, IoSliceMut, LineWriter, Lines, SpecReadByte, }; @@ -1092,7 +1091,7 @@ pub fn try_set_output_capture( OUTPUT_CAPTURE.try_with(move |slot| slot.replace(sink)) } -/// Write `args` to the capture buffer if enabled and possible, or `global_s` +/// Writes `args` to the capture buffer if enabled and possible, or `global_s` /// otherwise. `label` identifies the stream in a panic message. /// /// This function is used to print error messages, so it takes extra @@ -1190,9 +1189,8 @@ pub trait IsTerminal: crate::sealed::Sealed { /// /// - If you run this example by piping some text to it, e.g. `echo "foo" | path/to/executable` /// it will print: `Hello foo`. - /// - If you instead run the example interactively by running the executable directly, it will - /// panic with the message "Expected input to be piped to the process". - /// + /// - If you instead run the example interactively by running `path/to/executable` directly, it will + /// prompt for input. /// /// [changes]: io#platform-specific-behavior /// [`Stdin`]: crate::io::Stdin diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index a2c1c430863ab..bb6a53bb290f9 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -1,7 +1,8 @@ use super::{repeat, BorrowedBuf, Cursor, SeekFrom}; use crate::cmp::{self, min}; -use crate::io::{self, IoSlice, IoSliceMut, DEFAULT_BUF_SIZE}; -use crate::io::{BufRead, BufReader, Read, Seek, Write}; +use crate::io::{ + self, BufRead, BufReader, IoSlice, IoSliceMut, Read, Seek, Write, DEFAULT_BUF_SIZE, +}; use crate::mem::MaybeUninit; use crate::ops::Deref; @@ -530,7 +531,7 @@ fn io_slice_advance_slices_beyond_total_length() { assert!(bufs.is_empty()); } -/// Create a new writer that reads from at most `n_bufs` and reads +/// Creates a new writer that reads from at most `n_bufs` and reads /// `per_call` bytes (in total) per call to write. fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter { TestWriter { n_bufs, per_call, written: Vec::new() } @@ -675,13 +676,13 @@ fn cursor_read_exact_eof() { let mut r = slice.clone(); assert!(r.read_exact(&mut [0; 10]).is_err()); - assert!(r.is_empty()); + assert!(Cursor::split(&r).1.is_empty()); let mut r = slice; let buf = &mut [0; 10]; let mut buf = BorrowedBuf::from(buf.as_mut_slice()); assert!(r.read_buf_exact(buf.unfilled()).is_err()); - assert!(r.is_empty()); + assert!(Cursor::split(&r).1.is_empty()); assert_eq!(buf.filled(), b"123456"); } diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index 6de91e29c7707..1dff3f3832bd7 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -1,6 +1,5 @@ use crate::io::prelude::*; use crate::io::{empty, repeat, sink, BorrowedBuf, Empty, Repeat, SeekFrom, Sink}; - use crate::mem::MaybeUninit; #[test] diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 8415f36eba251..c82228fca4bcf 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -1175,7 +1175,7 @@ mod ref_keyword {} #[doc(keyword = "return")] // -/// Return a value from a function. +/// Returns a value from a function. /// /// A `return` marks the end of an execution path in a function: /// @@ -2310,7 +2310,7 @@ mod where_keyword {} #[doc(alias = "promise")] #[doc(keyword = "async")] // -/// Return a [`Future`] instead of blocking the current thread. +/// Returns a [`Future`] instead of blocking the current thread. /// /// Use `async` in front of `fn`, `closure`, or a `block` to turn the marked code into a `Future`. /// As such the code will not be run immediately, but will only be evaluated when the returned diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 4a18db3d5a3fc..05e33d47bac39 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -252,7 +252,9 @@ #![allow(internal_features)] #![deny(rustc::existing_doc_keyword)] #![deny(fuzzy_provenance_casts)] +#![deny(unsafe_op_in_unsafe_fn)] #![allow(rustdoc::redundant_explicit_links)] +#![warn(rustdoc::unescaped_backticks)] // Ensure that std can be linked against panic_abort despite compiled with `-C panic=unwind` #![deny(ffi_unwind_calls)] // std may use features in a platform-specific way @@ -266,10 +268,7 @@ )] #![cfg_attr(any(windows, target_os = "uefi"), feature(round_char_boundary))] #![cfg_attr(target_family = "wasm", feature(stdarch_wasm_atomic_wait))] -#![cfg_attr( - all(any(target_arch = "x86_64", target_arch = "x86"), target_os = "uefi"), - feature(stdarch_x86_has_cpuid) -)] +#![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] // // Language features: // tidy-alphabetical-start @@ -278,13 +277,11 @@ #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(asm_experimental_arch)] -#![feature(c_unwind)] #![feature(cfg_sanitizer_cfi)] #![feature(cfg_target_thread_local)] #![feature(cfi_encoding)] #![feature(concat_idents)] #![feature(const_mut_refs)] -#![feature(const_trait_impl)] #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(doc_cfg)] @@ -292,6 +289,7 @@ #![feature(doc_masked)] #![feature(doc_notable_trait)] #![feature(dropck_eyepatch)] +#![feature(extended_varargs_abi_support)] #![feature(f128)] #![feature(f16)] #![feature(if_let_guard)] @@ -300,6 +298,7 @@ #![feature(let_chains)] #![feature(link_cfg)] #![feature(linkage)] +#![feature(macro_metavar_expr_concat)] #![feature(min_exhaustive_patterns)] #![feature(min_specialization)] #![feature(must_not_suspend)] @@ -324,7 +323,6 @@ #![feature(core_io_borrowed_buf)] #![feature(duration_constants)] #![feature(error_generic_member_access)] -#![feature(error_in_core)] #![feature(error_iter)] #![feature(exact_size_is_empty)] #![feature(exclusive_wrapper)] @@ -336,13 +334,10 @@ #![feature(fmt_internals)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] -#![feature(hint_assert_unchecked)] #![feature(ip)] #![feature(maybe_uninit_slice)] -#![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_write_slice)] #![feature(panic_can_unwind)] -#![feature(panic_info_message)] #![feature(panic_internals)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] @@ -395,7 +390,6 @@ #![feature(edition_panic)] #![feature(format_args_nl)] #![feature(get_many_mut)] -#![feature(lazy_cell)] #![feature(log_syntax)] #![feature(test)] #![feature(trace_macros)] @@ -410,8 +404,6 @@ #![feature(const_ip)] #![feature(const_ipv4)] #![feature(const_ipv6)] -#![feature(const_maybe_uninit_uninit_array)] -#![feature(const_waker)] #![feature(thread_local_internals)] // tidy-alphabetical-end // @@ -475,25 +467,6 @@ pub mod rt; // The Rust prelude pub mod prelude; -// Public module declarations and re-exports -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::borrow; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::boxed; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::fmt; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::format; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::rc; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::slice; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::str; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::string; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::vec; #[stable(feature = "rust1", since = "1.0.0")] pub use core::any; #[stable(feature = "core_array", since = "1.36.0")] @@ -571,6 +544,25 @@ pub use core::u8; #[allow(deprecated, deprecated_in_future)] pub use core::usize; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::borrow; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::boxed; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::fmt; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::format; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::rc; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::slice; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::str; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::string; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::vec; + #[unstable(feature = "f128", issue = "116909")] pub mod f128; #[unstable(feature = "f16", issue = "116909")] @@ -596,6 +588,8 @@ pub mod panic; #[unstable(feature = "core_pattern_types", issue = "none")] pub mod pat; pub mod path; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub mod pipe; pub mod process; pub mod sync; pub mod time; @@ -612,23 +606,23 @@ mod std_float; pub mod simd { #![doc = include_str!("../../portable-simd/crates/core_simd/src/core_simd_docs.md")] - #[doc(inline)] - pub use crate::std_float::StdFloat; #[doc(inline)] pub use core::simd::*; + + #[doc(inline)] + pub use crate::std_float::StdFloat; } #[stable(feature = "futures_api", since = "1.36.0")] pub mod task { //! Types and Traits for working with asynchronous tasks. - #[doc(inline)] - #[stable(feature = "futures_api", since = "1.36.0")] - pub use core::task::*; - #[doc(inline)] #[stable(feature = "wake_trait", since = "1.51.0")] pub use alloc::task::*; + #[doc(inline)] + #[stable(feature = "futures_api", since = "1.36.0")] + pub use core::task::*; } #[doc = include_str!("../../stdarch/crates/core_arch/src/core_arch_docs.md")] @@ -670,38 +664,34 @@ pub mod alloc; mod panicking; #[path = "../../backtrace/src/lib.rs"] -#[allow(dead_code, unused_attributes, fuzzy_provenance_casts)] +#[allow(dead_code, unused_attributes, fuzzy_provenance_casts, unsafe_op_in_unsafe_fn)] mod backtrace_rs; -// Re-export macros defined in core. -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated, deprecated_in_future)] -pub use core::{ - assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches, todo, r#try, - unimplemented, unreachable, write, writeln, -}; - -// Re-export built-in macros defined through core. -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow(deprecated)] -pub use core::{ - assert, assert_matches, cfg, column, compile_error, concat, concat_idents, const_format_args, - env, file, format_args, format_args_nl, include, include_bytes, include_str, line, log_syntax, - module_path, option_env, stringify, trace_macros, -}; - +#[unstable(feature = "cfg_match", issue = "115585")] +pub use core::cfg_match; #[unstable( feature = "concat_bytes", issue = "87555", reason = "`concat_bytes` is not stable enough for use and is subject to change" )] pub use core::concat_bytes; - -#[unstable(feature = "cfg_match", issue = "115585")] -pub use core::cfg_match; - #[stable(feature = "core_primitive", since = "1.43.0")] pub use core::primitive; +// Re-export built-in macros defined through core. +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[allow(deprecated)] +pub use core::{ + assert, assert_matches, cfg, column, compile_error, concat, concat_idents, const_format_args, + env, file, format_args, format_args_nl, include, include_bytes, include_str, line, log_syntax, + module_path, option_env, stringify, trace_macros, +}; +// Re-export macros defined in core. +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated, deprecated_in_future)] +pub use core::{ + assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches, todo, r#try, + unimplemented, unreachable, write, writeln, +}; // Include a number of private modules that exist solely to provide // the rustdoc documentation for primitive types. Using `include!` diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 58df83bd79d23..ba519afc62b07 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -230,7 +230,7 @@ macro_rules! eprintln { /// ```rust /// let a = 2; /// let b = dbg!(a * 2) + 1; -/// // ^-- prints: [src/main.rs:2] a * 2 = 4 +/// // ^-- prints: [src/main.rs:2:9] a * 2 = 4 /// assert_eq!(b, 5); /// ``` /// @@ -281,7 +281,7 @@ macro_rules! eprintln { /// This prints to [stderr]: /// /// ```text,ignore -/// [src/main.rs:4] n.checked_sub(4) = None +/// [src/main.rs:2:22] n.checked_sub(4) = None /// ``` /// /// Naive factorial implementation: @@ -301,15 +301,15 @@ macro_rules! eprintln { /// This prints to [stderr]: /// /// ```text,ignore -/// [src/main.rs:3] n <= 1 = false -/// [src/main.rs:3] n <= 1 = false -/// [src/main.rs:3] n <= 1 = false -/// [src/main.rs:3] n <= 1 = true -/// [src/main.rs:4] 1 = 1 -/// [src/main.rs:5] n * factorial(n - 1) = 2 -/// [src/main.rs:5] n * factorial(n - 1) = 6 -/// [src/main.rs:5] n * factorial(n - 1) = 24 -/// [src/main.rs:11] factorial(4) = 24 +/// [src/main.rs:2:8] n <= 1 = false +/// [src/main.rs:2:8] n <= 1 = false +/// [src/main.rs:2:8] n <= 1 = false +/// [src/main.rs:2:8] n <= 1 = true +/// [src/main.rs:3:9] 1 = 1 +/// [src/main.rs:7:9] n * factorial(n - 1) = 2 +/// [src/main.rs:7:9] n * factorial(n - 1) = 6 +/// [src/main.rs:7:9] n * factorial(n - 1) = 24 +/// [src/main.rs:9:1] factorial(4) = 24 /// ``` /// /// The `dbg!(..)` macro moves the input: @@ -373,10 +373,17 @@ macro_rules! dbg { }; } +/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. #[cfg(test)] macro_rules! assert_approx_eq { - ($a:expr, $b:expr) => {{ + ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; + ($a:expr, $b:expr, $lim:expr) => {{ let (a, b) = (&$a, &$b); - assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b); + let diff = (*a - *b).abs(); + assert!( + diff < $lim, + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, actual {diff:?})", + lim = $lim + ); }}; } diff --git a/library/std/src/net/ip_addr.rs b/library/std/src/net/ip_addr.rs index e167fbd1b9cf8..8a9426b61f999 100644 --- a/library/std/src/net/ip_addr.rs +++ b/library/std/src/net/ip_addr.rs @@ -2,17 +2,15 @@ #[cfg(all(test, not(target_os = "emscripten")))] mod tests; -use crate::sys::net::netc as c; -use crate::sys_common::{FromInner, IntoInner}; - #[stable(feature = "ip_addr", since = "1.7.0")] pub use core::net::IpAddr; - +#[unstable(feature = "ip", issue = "27709")] +pub use core::net::Ipv6MulticastScope; #[stable(feature = "rust1", since = "1.0.0")] pub use core::net::{Ipv4Addr, Ipv6Addr}; -#[unstable(feature = "ip", issue = "27709")] -pub use core::net::Ipv6MulticastScope; +use crate::sys::net::netc as c; +use crate::sys_common::{FromInner, IntoInner}; impl IntoInner for Ipv4Addr { #[inline] diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index bcab15db35b5c..3b19c743b1e24 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -21,20 +21,20 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::io::{self, ErrorKind}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::net::AddrParseError; #[stable(feature = "rust1", since = "1.0.0")] pub use self::ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::socket_addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; -#[unstable(feature = "tcplistener_into_incoming", issue = "88339")] +#[unstable(feature = "tcplistener_into_incoming", issue = "88373")] pub use self::tcp::IntoIncoming; #[stable(feature = "rust1", since = "1.0.0")] pub use self::tcp::{Incoming, TcpListener, TcpStream}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::udp::UdpSocket; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::net::AddrParseError; +use crate::io::{self, ErrorKind}; mod ip_addr; mod socket_addr; diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index 421fed9077c5b..84922aabdb569 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -2,19 +2,14 @@ #[cfg(all(test, not(target_os = "emscripten")))] mod tests; -use crate::io; -use crate::iter; -use crate::mem; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; + use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::option; -use crate::slice; use crate::sys::net::netc as c; use crate::sys_common::net::LookupHost; use crate::sys_common::{FromInner, IntoInner}; -use crate::vec; - -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; +use crate::{io, iter, mem, option, slice, vec}; impl FromInner for SocketAddrV4 { fn from_inner(addr: c::sockaddr_in) -> SocketAddrV4 { diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 9667d5f920e43..22d2dfe65a249 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -3,14 +3,12 @@ #[cfg(all(test, not(any(target_os = "emscripten", target_os = "xous"))))] mod tests; -use crate::io::prelude::*; - use crate::fmt; +use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::iter::FusedIterator; use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sys_common::net as net_imp; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys_common::{net as net_imp, AsInner, FromInner, IntoInner}; use crate::time::Duration; /// A TCP stream between a local and a remote socket. @@ -105,7 +103,7 @@ pub struct Incoming<'a> { /// /// [`accept`]: TcpListener::accept #[derive(Debug)] -#[unstable(feature = "tcplistener_into_incoming", issue = "88339")] +#[unstable(feature = "tcplistener_into_incoming", issue = "88373")] pub struct IntoIncoming { listener: TcpListener, } @@ -894,7 +892,7 @@ impl TcpListener { /// } /// ``` #[must_use = "`self` will be dropped if the result is not used"] - #[unstable(feature = "tcplistener_into_incoming", issue = "88339")] + #[unstable(feature = "tcplistener_into_incoming", issue = "88373")] pub fn into_incoming(self) -> IntoIncoming { IntoIncoming { listener: self } } @@ -1033,7 +1031,7 @@ impl<'a> Iterator for Incoming<'a> { #[stable(feature = "tcp_listener_incoming_fused_iterator", since = "1.64.0")] impl FusedIterator for Incoming<'_> {} -#[unstable(feature = "tcplistener_into_incoming", issue = "88339")] +#[unstable(feature = "tcplistener_into_incoming", issue = "88373")] impl Iterator for IntoIncoming { type Item = io::Result; fn next(&mut self) -> Option> { @@ -1041,7 +1039,7 @@ impl Iterator for IntoIncoming { } } -#[unstable(feature = "tcplistener_into_incoming", issue = "88339")] +#[unstable(feature = "tcplistener_into_incoming", issue = "88373")] impl FusedIterator for IntoIncoming {} impl AsInner for TcpListener { diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index ec8b62f968754..d26517d74e492 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -1,12 +1,11 @@ -use crate::fmt; use crate::io::prelude::*; use crate::io::{BorrowedBuf, IoSlice, IoSliceMut}; use crate::mem::MaybeUninit; use crate::net::test::{next_test_ip4, next_test_ip6}; use crate::net::*; use crate::sync::mpsc::channel; -use crate::thread; use crate::time::{Duration, Instant}; +use crate::{fmt, thread}; fn each_ip(f: &mut dyn FnMut(SocketAddr)) { f(next_test_ip4()); @@ -301,7 +300,7 @@ fn read_buf() { }); let mut s = t!(srv.accept()).0; - let mut buf: [MaybeUninit; 128] = MaybeUninit::uninit_array(); + let mut buf: [MaybeUninit; 128] = [MaybeUninit::uninit(); 128]; let mut buf = BorrowedBuf::from(buf.as_mut_slice()); t!(s.read_buf(buf.unfilled())); assert_eq!(buf.filled(), &[1, 2, 3, 4]); diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 60347a11da9c5..32e9086003d6b 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -4,8 +4,7 @@ mod tests; use crate::fmt; use crate::io::{self, ErrorKind}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; -use crate::sys_common::net as net_imp; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys_common::{net as net_imp, AsInner, FromInner, IntoInner}; use crate::time::Duration; /// A UDP socket. diff --git a/library/std/src/num.rs b/library/std/src/num.rs index 8910cdea7c0c9..c1e6e7e628c83 100644 --- a/library/std/src/num.rs +++ b/library/std/src/num.rs @@ -9,32 +9,27 @@ #[cfg(test)] mod tests; +#[stable(feature = "int_error_matching", since = "1.55.0")] +pub use core::num::IntErrorKind; +#[stable(feature = "generic_nonzero", since = "1.79.0")] +pub use core::num::NonZero; #[stable(feature = "saturating_int_impl", since = "1.74.0")] pub use core::num::Saturating; #[stable(feature = "rust1", since = "1.0.0")] pub use core::num::Wrapping; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError}; - #[unstable( feature = "nonzero_internals", reason = "implementation detail which may disappear or be replaced at any time", issue = "none" )] pub use core::num::ZeroablePrimitive; - -#[stable(feature = "generic_nonzero", since = "1.79.0")] -pub use core::num::NonZero; - +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError}; #[stable(feature = "signed_nonzero", since = "1.34.0")] pub use core::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize}; - #[stable(feature = "nonzero", since = "1.28.0")] pub use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; -#[stable(feature = "int_error_matching", since = "1.55.0")] -pub use core::num::IntErrorKind; - #[cfg(test)] use crate::fmt; #[cfg(test)] diff --git a/library/std/src/os/aix/raw.rs b/library/std/src/os/aix/raw.rs index b4c8dc72cfe5b..13f61fc97226b 100644 --- a/library/std/src/os/aix/raw.rs +++ b/library/std/src/os/aix/raw.rs @@ -4,6 +4,5 @@ #[stable(feature = "pthread_t", since = "1.8.0")] pub use libc::pthread_t; - #[stable(feature = "raw_ext", since = "1.1.0")] pub use libc::{blkcnt_t, blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t, stat, time_t}; diff --git a/library/std/src/os/android/fs.rs b/library/std/src/os/android/fs.rs index 1beb3cf6e84b5..6b931e3816950 100644 --- a/library/std/src/os/android/fs.rs +++ b/library/std/src/os/android/fs.rs @@ -1,10 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; - #[allow(deprecated)] use crate::os::android::raw; +use crate::sys_common::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/android/net.rs b/library/std/src/os/android/net.rs index 349e73eaabdaf..960a304fd0c8d 100644 --- a/library/std/src/os/android/net.rs +++ b/library/std/src/os/android/net.rs @@ -4,9 +4,7 @@ #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub use crate::os::net::linux_ext::addr::SocketAddrExt; - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub use crate::os::net::linux_ext::socket::UnixSocketExt; - #[unstable(feature = "tcp_quickack", issue = "96256")] pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/macos/fs.rs b/library/std/src/os/darwin/fs.rs similarity index 98% rename from library/std/src/os/macos/fs.rs rename to library/std/src/os/darwin/fs.rs index 573426d1a8646..2d154b214b5f0 100644 --- a/library/std/src/os/macos/fs.rs +++ b/library/std/src/os/darwin/fs.rs @@ -1,13 +1,12 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] +#![allow(dead_code)] +#[allow(deprecated)] +use super::raw; use crate::fs::{self, Metadata}; use crate::sealed::Sealed; use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; use crate::time::SystemTime; -#[allow(deprecated)] -use crate::os::macos::raw; - /// OS-specific extensions to [`fs::Metadata`]. /// /// [`fs::Metadata`]: crate::fs::Metadata @@ -70,6 +69,7 @@ pub trait MetadataExt { fn st_gen(&self) -> u32; #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_lspare(&self) -> u32; + #[cfg(target_os = "macos")] #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_qspare(&self) -> [u64; 2]; } @@ -143,6 +143,7 @@ impl MetadataExt for Metadata { fn st_lspare(&self) -> u32 { self.as_inner().as_inner().st_lspare as u32 } + #[cfg(target_os = "macos")] fn st_qspare(&self) -> [u64; 2] { let qspare = self.as_inner().as_inner().st_qspare; [qspare[0] as u64, qspare[1] as u64] diff --git a/library/std/src/os/darwin/mod.rs b/library/std/src/os/darwin/mod.rs new file mode 100644 index 0000000000000..03401fe8895b9 --- /dev/null +++ b/library/std/src/os/darwin/mod.rs @@ -0,0 +1,20 @@ +//! Platform-specific extensions to `std` for Darwin / Apple platforms. +//! +//! This is available on the following operating systems: +//! - macOS +//! - iOS +//! - tvOS +//! - watchOS +//! - visionOS +//! +//! Note: This module is called "Darwin" as that's the name of the underlying +//! core OS of the above operating systems, but it should not be confused with +//! the `-darwin` suffix in the `x86_64-apple-darwin` and +//! `aarch64-apple-darwin` target names, which are mostly named that way for +//! legacy reasons. + +pub(crate) mod fs; +// deprecated, but used for public reexport under `std::os::unix::raw`, as +// well as `std::os::macos`/`std::os::ios`, because those modules precede the +// decision to remove these. +pub(super) mod raw; diff --git a/library/std/src/os/ios/raw.rs b/library/std/src/os/darwin/raw.rs similarity index 87% rename from library/std/src/os/ios/raw.rs rename to library/std/src/os/darwin/raw.rs index af12aeebe5d0c..047727f45325f 100644 --- a/library/std/src/os/ios/raw.rs +++ b/library/std/src/os/darwin/raw.rs @@ -1,15 +1,4 @@ -//! iOS-specific raw type definitions - -#![stable(feature = "raw_ext", since = "1.1.0")] -#![deprecated( - since = "1.8.0", - note = "these type aliases are no longer supported by \ - the standard library, the `libc` crate on \ - crates.io should be used instead for the correct \ - definitions" -)] -#![allow(deprecated)] - +//! Apple-specific raw type definitions use crate::os::raw::c_long; #[stable(feature = "raw_ext", since = "1.1.0")] @@ -35,6 +24,7 @@ pub type pthread_t = usize; #[repr(C)] #[derive(Clone)] #[stable(feature = "raw_ext", since = "1.1.0")] +#[allow(dead_code)] pub struct stat { #[stable(feature = "raw_ext", since = "1.1.0")] pub st_dev: i32, diff --git a/library/std/src/os/dragonfly/fs.rs b/library/std/src/os/dragonfly/fs.rs index 1424fc4c69880..0cd543b2ab97d 100644 --- a/library/std/src/os/dragonfly/fs.rs +++ b/library/std/src/os/dragonfly/fs.rs @@ -1,10 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; - #[allow(deprecated)] use crate::os::dragonfly::raw; +use crate::sys_common::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/emscripten/fs.rs b/library/std/src/os/emscripten/fs.rs index d5ec8e03c00d1..3282b79ac1c81 100644 --- a/library/std/src/os/emscripten/fs.rs +++ b/library/std/src/os/emscripten/fs.rs @@ -1,10 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; - #[allow(deprecated)] use crate::os::emscripten::raw; +use crate::sys_common::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/espidf/fs.rs b/library/std/src/os/espidf/fs.rs index 88701dafe20ce..ffff584cda02a 100644 --- a/library/std/src/os/espidf/fs.rs +++ b/library/std/src/os/espidf/fs.rs @@ -1,10 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; - #[allow(deprecated)] use crate::os::espidf::raw; +use crate::sys_common::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 8c7fc4cb2e453..2d087c03b04b4 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -4,14 +4,12 @@ #![deny(unsafe_op_in_unsafe_fn)] use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use crate::fmt; -use crate::fs; -use crate::io; use crate::marker::PhantomData; -use crate::mem::forget; +use crate::mem::ManuallyDrop; #[cfg(not(any(target_arch = "wasm32", target_env = "sgx", target_os = "hermit")))] use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::{fmt, fs, io}; /// A borrowed file descriptor. /// @@ -24,9 +22,14 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; /// passed as an argument, it is not captured or consumed, and it never has the /// value `-1`. /// -/// This type's `.to_owned()` implementation returns another `BorrowedFd` -/// rather than an `OwnedFd`. It just makes a trivial copy of the raw file -/// descriptor, which is then borrowed under the same lifetime. +/// This type does not have a [`ToOwned`][crate::borrow::ToOwned] +/// implementation. Calling `.to_owned()` on a variable of this type will call +/// it on `&BorrowedFd` and use `Clone::clone()` like `ToOwned` does for all +/// types implementing `Clone`. The result will be descriptor borrowed under +/// the same lifetime. +/// +/// To obtain an [`OwnedFd`], you can use [`BorrowedFd::try_clone_to_owned`] +/// instead, but this is not supported on all platforms. #[derive(Copy, Clone)] #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(0)] @@ -50,6 +53,8 @@ pub struct BorrowedFd<'fd> { /// descriptor, so it can be used in FFI in places where a file descriptor is /// passed as a consumed argument or returned as an owned value, and it never /// has the value `-1`. +/// +/// You can use [`AsFd::as_fd`] to obtain a [`BorrowedFd`]. #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(0)] // libstd/os/raw/mod.rs assures me that every libstd-supported platform has a @@ -63,7 +68,7 @@ pub struct OwnedFd { } impl BorrowedFd<'_> { - /// Return a `BorrowedFd` holding the given raw file descriptor. + /// Returns a `BorrowedFd` holding the given raw file descriptor. /// /// # Safety /// @@ -141,9 +146,7 @@ impl AsRawFd for OwnedFd { impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - let fd = self.fd; - forget(self); - fd + ManuallyDrop::new(self).fd } } @@ -175,6 +178,11 @@ impl Drop for OwnedFd { // the file descriptor was closed or not, and if we retried (for // something like EINTR), we might close another valid file descriptor // opened after we closed ours. + // However, this is usually justified, as some of the major Unices + // do make sure to always close the FD, even when `close()` is interrupted, + // and the scenario is rare to begin with. + // Helpful link to an epic discussion by POSIX workgroup: + // http://austingroupbugs.net/view.php?id=529 #[cfg(not(target_os = "hermit"))] { #[cfg(unix)] diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index ef896ea95c9c9..0d99d5492a268 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -2,8 +2,9 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::fs; -use crate::io; +#[cfg(target_os = "hermit")] +use hermit_abi as libc; + #[cfg(target_os = "hermit")] use crate::os::hermit::io::OwnedFd; #[cfg(not(target_os = "hermit"))] @@ -15,8 +16,7 @@ use crate::os::unix::io::OwnedFd; #[cfg(target_os = "wasi")] use crate::os::wasi::io::OwnedFd; use crate::sys_common::{AsInner, IntoInner}; -#[cfg(target_os = "hermit")] -use hermit_abi as libc; +use crate::{fs, io}; /// Raw file descriptors. #[rustc_allowed_through_unstable_modules] @@ -138,6 +138,7 @@ pub trait IntoRawFd { /// let raw_fd: RawFd = f.into_raw_fd(); /// # Ok::<(), io::Error>(()) /// ``` + #[must_use = "losing the raw file descriptor may leak resources"] #[stable(feature = "into_raw_os", since = "1.4.0")] fn into_raw_fd(self) -> RawFd; } diff --git a/library/std/src/os/fortanix_sgx/arch.rs b/library/std/src/os/fortanix_sgx/arch.rs index 8358cb9e81b65..4c8048e7152e2 100644 --- a/library/std/src/os/fortanix_sgx/arch.rs +++ b/library/std/src/os/fortanix_sgx/arch.rs @@ -4,9 +4,10 @@ //! Software Developer's Manual, Volume 3, Chapter 40. #![unstable(feature = "sgx_platform", issue = "56975")] -use crate::mem::MaybeUninit; use core::arch::asm; +use crate::mem::MaybeUninit; + /// Wrapper struct to force 16-byte alignment. #[repr(align(16))] #[unstable(feature = "sgx_platform", issue = "56975")] diff --git a/library/std/src/os/fortanix_sgx/mod.rs b/library/std/src/os/fortanix_sgx/mod.rs index 39a42f4e17fec..64f4d97ca95e2 100644 --- a/library/std/src/os/fortanix_sgx/mod.rs +++ b/library/std/src/os/fortanix_sgx/mod.rs @@ -22,21 +22,12 @@ pub mod usercalls { /// Lowest-level interfaces to usercalls and usercall ABI type definitions. pub mod raw { pub use crate::sys::abi::usercalls::raw::{ - accept_stream, alloc, async_queues, bind_stream, close, connect_stream, exit, flush, - free, insecure_time, launch_thread, read, read_alloc, send, wait, write, - }; - pub use crate::sys::abi::usercalls::raw::{do_usercall, Usercalls as UsercallNrs}; - pub use crate::sys::abi::usercalls::raw::{Register, RegisterArgument, ReturnValue}; - - // fortanix-sgx-abi re-exports - pub use crate::sys::abi::usercalls::raw::Error; - pub use crate::sys::abi::usercalls::raw::{ - ByteBuffer, Cancel, FifoDescriptor, Return, Usercall, - }; - pub use crate::sys::abi::usercalls::raw::{Fd, Result, Tcs}; - pub use crate::sys::abi::usercalls::raw::{ - EV_RETURNQ_NOT_EMPTY, EV_UNPARK, EV_USERCALLQ_NOT_FULL, FD_STDERR, FD_STDIN, FD_STDOUT, - RESULT_SUCCESS, USERCALL_USER_DEFINED, WAIT_INDEFINITE, WAIT_NO, + accept_stream, alloc, async_queues, bind_stream, close, connect_stream, do_usercall, + exit, flush, free, insecure_time, launch_thread, read, read_alloc, send, wait, write, + ByteBuffer, Cancel, Error, Fd, FifoDescriptor, Register, RegisterArgument, Result, + Return, ReturnValue, Tcs, Usercall, Usercalls as UsercallNrs, EV_RETURNQ_NOT_EMPTY, + EV_UNPARK, EV_USERCALLQ_NOT_FULL, FD_STDERR, FD_STDIN, FD_STDOUT, RESULT_SUCCESS, + USERCALL_USER_DEFINED, WAIT_INDEFINITE, WAIT_NO, }; } } diff --git a/library/std/src/os/freebsd/fs.rs b/library/std/src/os/freebsd/fs.rs index 5689a82e00a34..34384a4bcb505 100644 --- a/library/std/src/os/freebsd/fs.rs +++ b/library/std/src/os/freebsd/fs.rs @@ -1,10 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; - #[allow(deprecated)] use crate::os::freebsd::raw; +use crate::sys_common::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/freebsd/net.rs b/library/std/src/os/freebsd/net.rs index b7e0fdc0a9aab..fcfc5c1c83935 100644 --- a/library/std/src/os/freebsd/net.rs +++ b/library/std/src/os/freebsd/net.rs @@ -42,7 +42,7 @@ pub trait UnixSocketExt: Sealed { #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()>; - /// Get a filter name if one had been set previously on the socket. + /// Gets a filter name if one had been set previously on the socket. #[unstable(feature = "acceptfilter", issue = "121891")] fn acceptfilter(&self) -> io::Result<&CStr>; diff --git a/library/std/src/os/haiku/fs.rs b/library/std/src/os/haiku/fs.rs index a23a2af8f6e7b..23f6493180b76 100644 --- a/library/std/src/os/haiku/fs.rs +++ b/library/std/src/os/haiku/fs.rs @@ -1,10 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; - #[allow(deprecated)] use crate::os::haiku::raw; +use crate::sys_common::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/hermit/io/mod.rs b/library/std/src/os/hermit/io/mod.rs index 524dfae0d63ae..df93f63a003cf 100644 --- a/library/std/src/os/hermit/io/mod.rs +++ b/library/std/src/os/hermit/io/mod.rs @@ -1,13 +1,4 @@ -#![stable(feature = "os_fd", since = "1.66.0")] +#![stable(feature = "rust1", since = "1.0.0")] -mod net; -#[path = "../../fd/owned.rs"] -mod owned; -#[path = "../../fd/raw.rs"] -mod raw; - -// Export the types and traits for the public API. -#[stable(feature = "os_fd", since = "1.66.0")] -pub use owned::*; -#[stable(feature = "os_fd", since = "1.66.0")] -pub use raw::*; +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::os::fd::*; diff --git a/library/std/src/os/hermit/io/net.rs b/library/std/src/os/hermit/io/net.rs index 8f3802d7873dc..7a774345b231a 100644 --- a/library/std/src/os/hermit/io/net.rs +++ b/library/std/src/os/hermit/io/net.rs @@ -1,5 +1,4 @@ -use crate::os::hermit::io::OwnedFd; -use crate::os::hermit::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::hermit::io::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::sys_common::{self, AsInner, FromInner, IntoInner}; use crate::{net, sys}; diff --git a/library/std/src/os/hermit/mod.rs b/library/std/src/os/hermit/mod.rs index 02a4b2c3ab5e7..5812206a25759 100644 --- a/library/std/src/os/hermit/mod.rs +++ b/library/std/src/os/hermit/mod.rs @@ -1,4 +1,5 @@ #![stable(feature = "rust1", since = "1.0.0")] +#![deny(unsafe_op_in_unsafe_fn)] #[allow(unused_extern_crates)] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/os/horizon/mod.rs b/library/std/src/os/horizon/mod.rs index 326d0ae9cb96d..14ce409f42c0b 100644 --- a/library/std/src/os/horizon/mod.rs +++ b/library/std/src/os/horizon/mod.rs @@ -1,5 +1,6 @@ //! Definitions for Horizon OS +#![forbid(unsafe_op_in_unsafe_fn)] #![stable(feature = "raw_ext", since = "1.1.0")] pub mod fs; diff --git a/library/std/src/os/horizon/raw.rs b/library/std/src/os/horizon/raw.rs index 929fa7db1f964..e5368ea265a13 100644 --- a/library/std/src/os/horizon/raw.rs +++ b/library/std/src/os/horizon/raw.rs @@ -38,6 +38,7 @@ pub type time_t = libc::time_t; #[repr(C)] #[derive(Clone)] #[stable(feature = "raw_ext", since = "1.1.0")] +#[allow(dead_code)] // This exists for parity with other `raw` modules, but isn't actually used. pub struct stat { #[stable(feature = "raw_ext", since = "1.1.0")] pub st_dev: dev_t, diff --git a/library/std/src/os/illumos/fs.rs b/library/std/src/os/illumos/fs.rs index 63be48b8131b2..75dbe167785db 100644 --- a/library/std/src/os/illumos/fs.rs +++ b/library/std/src/os/illumos/fs.rs @@ -1,10 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; - #[allow(deprecated)] use crate::os::illumos::raw; +use crate::sys_common::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/ios/fs.rs b/library/std/src/os/ios/fs.rs deleted file mode 100644 index e5df4de0b7f71..0000000000000 --- a/library/std/src/os/ios/fs.rs +++ /dev/null @@ -1,160 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::{self, Metadata}; -use crate::sealed::Sealed; -use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; -use crate::time::SystemTime; - -#[allow(deprecated)] -use super::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: crate::fs::Metadata -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[deprecated( - since = "1.8.0", - note = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blocks(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_flags(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gen(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_lspare(&self) -> u32; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for Metadata { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_birthtime(&self) -> i64 { - self.as_inner().as_inner().st_birthtime as i64 - } - fn st_birthtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_birthtime_nsec as i64 - } - fn st_blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn st_blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } - fn st_gen(&self) -> u32 { - self.as_inner().as_inner().st_gen as u32 - } - fn st_flags(&self) -> u32 { - self.as_inner().as_inner().st_flags as u32 - } - fn st_lspare(&self) -> u32 { - self.as_inner().as_inner().st_lspare as u32 - } -} - -/// OS-specific extensions to [`fs::FileTimes`]. -#[stable(feature = "file_set_times", since = "1.75.0")] -pub trait FileTimesExt: Sealed { - /// Set the creation time of a file. - #[stable(feature = "file_set_times", since = "1.75.0")] - fn set_created(self, t: SystemTime) -> Self; -} - -#[stable(feature = "file_set_times", since = "1.75.0")] -impl FileTimesExt for fs::FileTimes { - fn set_created(mut self, t: SystemTime) -> Self { - self.as_inner_mut().set_created(t.into_inner()); - self - } -} diff --git a/library/std/src/os/ios/mod.rs b/library/std/src/os/ios/mod.rs index fdefa1f6b21c4..52d592ed95afa 100644 --- a/library/std/src/os/ios/mod.rs +++ b/library/std/src/os/ios/mod.rs @@ -2,5 +2,28 @@ #![stable(feature = "raw_ext", since = "1.1.0")] -pub mod fs; -pub mod raw; +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub mod fs { + #[doc(inline)] + #[stable(feature = "file_set_times", since = "1.75.0")] + pub use crate::os::darwin::fs::FileTimesExt; + #[doc(inline)] + #[stable(feature = "metadata_ext", since = "1.1.0")] + pub use crate::os::darwin::fs::MetadataExt; +} + +/// iOS-specific raw type definitions +#[stable(feature = "raw_ext", since = "1.1.0")] +#[deprecated( + since = "1.8.0", + note = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions" +)] +#[allow(deprecated)] +pub mod raw { + #[doc(inline)] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub use crate::os::darwin::raw::*; +} diff --git a/library/std/src/os/l4re/fs.rs b/library/std/src/os/l4re/fs.rs index 6d6a535b1e831..0511ddcf19af6 100644 --- a/library/std/src/os/l4re/fs.rs +++ b/library/std/src/os/l4re/fs.rs @@ -5,10 +5,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; - #[allow(deprecated)] use crate::os::l4re::raw; +use crate::sys_common::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/linux/fs.rs b/library/std/src/os/linux/fs.rs index ab0b2a3eda3f5..20a7a161a2628 100644 --- a/library/std/src/os/linux/fs.rs +++ b/library/std/src/os/linux/fs.rs @@ -5,10 +5,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; - #[allow(deprecated)] use crate::os::linux::raw; +use crate::sys_common::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/linux/net.rs b/library/std/src/os/linux/net.rs index f898e70548706..1de120c8fd366 100644 --- a/library/std/src/os/linux/net.rs +++ b/library/std/src/os/linux/net.rs @@ -4,9 +4,7 @@ #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub use crate::os::net::linux_ext::addr::SocketAddrExt; - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub use crate::os::net::linux_ext::socket::UnixSocketExt; - #[unstable(feature = "tcp_quickack", issue = "96256")] pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/linux/process.rs b/library/std/src/os/linux/process.rs index 2ba67a6dd1aa9..9195909479729 100644 --- a/library/std/src/os/linux/process.rs +++ b/library/std/src/os/linux/process.rs @@ -6,20 +6,20 @@ use crate::io::Result; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::process; +use crate::process::{self, ExitStatus}; use crate::sealed::Sealed; #[cfg(not(doc))] -use crate::sys::fd::FileDesc; +use crate::sys::{fd::FileDesc, linux::pidfd::PidFd as InnerPidFd}; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; #[cfg(doc)] -struct FileDesc; +struct InnerPidFd; /// This type represents a file descriptor that refers to a process. /// /// A `PidFd` can be obtained by setting the corresponding option on [`Command`] /// with [`create_pidfd`]. Subsequently, the created pidfd can be retrieved -/// from the [`Child`] by calling [`pidfd`] or [`take_pidfd`]. +/// from the [`Child`] by calling [`pidfd`] or [`into_pidfd`]. /// /// Example: /// ```no_run @@ -33,7 +33,7 @@ struct FileDesc; /// .expect("Failed to spawn child"); /// /// let pidfd = child -/// .take_pidfd() +/// .into_pidfd() /// .expect("Failed to retrieve pidfd"); /// /// // The file descriptor will be closed when `pidfd` is dropped. @@ -44,28 +44,63 @@ struct FileDesc; /// [`create_pidfd`]: CommandExt::create_pidfd /// [`Child`]: process::Child /// [`pidfd`]: fn@ChildExt::pidfd -/// [`take_pidfd`]: ChildExt::take_pidfd +/// [`into_pidfd`]: ChildExt::into_pidfd /// [`pidfd_open(2)`]: https://man7.org/linux/man-pages/man2/pidfd_open.2.html #[derive(Debug)] +#[repr(transparent)] pub struct PidFd { - inner: FileDesc, + inner: InnerPidFd, } -impl AsInner for PidFd { +impl PidFd { + /// Forces the child process to exit. + /// + /// Unlike [`Child::kill`] it is possible to attempt to kill + /// reaped children since PidFd does not suffer from pid recycling + /// races. But doing so will return an Error. + /// + /// [`Child::kill`]: process::Child::kill + pub fn kill(&self) -> Result<()> { + self.inner.kill() + } + + /// Waits for the child to exit completely, returning the status that it exited with. + /// + /// Unlike [`Child::wait`] it does not ensure that the stdin handle is closed. + /// Additionally it will not return an `ExitStatus` if the child + /// has already been reaped. Instead an error will be returned. + /// + /// [`Child::wait`]: process::Child::wait + pub fn wait(&self) -> Result { + self.inner.wait().map(FromInner::from_inner) + } + + /// Attempts to collect the exit status of the child if it has already exited. + /// + /// Unlike [`Child::try_wait`] this method will return an Error + /// if the child has already been reaped. + /// + /// [`Child::try_wait`]: process::Child::try_wait + pub fn try_wait(&self) -> Result> { + Ok(self.inner.try_wait()?.map(FromInner::from_inner)) + } +} + +impl AsInner for PidFd { #[inline] - fn as_inner(&self) -> &FileDesc { + fn as_inner(&self) -> &InnerPidFd { &self.inner } } -impl FromInner for PidFd { - fn from_inner(inner: FileDesc) -> PidFd { +impl FromInner for PidFd { + fn from_inner(inner: InnerPidFd) -> PidFd { PidFd { inner } } } -impl IntoInner for PidFd { - fn into_inner(self) -> FileDesc { +impl IntoInner for PidFd { + fn into_inner(self) -> InnerPidFd { self.inner } } @@ -73,37 +108,37 @@ impl IntoInner for PidFd { impl AsRawFd for PidFd { #[inline] fn as_raw_fd(&self) -> RawFd { - self.as_inner().as_raw_fd() + self.as_inner().as_inner().as_raw_fd() } } impl FromRawFd for PidFd { unsafe fn from_raw_fd(fd: RawFd) -> Self { - Self::from_inner(FileDesc::from_raw_fd(fd)) + Self::from_inner(InnerPidFd::from_raw_fd(fd)) } } impl IntoRawFd for PidFd { fn into_raw_fd(self) -> RawFd { - self.into_inner().into_raw_fd() + self.into_inner().into_inner().into_raw_fd() } } impl AsFd for PidFd { fn as_fd(&self) -> BorrowedFd<'_> { - self.as_inner().as_fd() + self.as_inner().as_inner().as_fd() } } impl From for PidFd { fn from(fd: OwnedFd) -> Self { - Self::from_inner(FileDesc::from_inner(fd)) + Self::from_inner(InnerPidFd::from_inner(FileDesc::from_inner(fd))) } } impl From for OwnedFd { fn from(pid_fd: PidFd) -> Self { - pid_fd.into_inner().into_inner() + pid_fd.into_inner().into_inner().into_inner() } } @@ -124,18 +159,26 @@ pub trait ChildExt: Sealed { /// [`Child`]: process::Child fn pidfd(&self) -> Result<&PidFd>; - /// Takes ownership of the [`PidFd`] created for this [`Child`], if available. + /// Returns the [`PidFd`] created for this [`Child`], if available. + /// Otherwise self is returned. /// /// A pidfd will only be available if its creation was requested with /// [`create_pidfd`] when the corresponding [`Command`] was created. /// + /// Taking ownership of the PidFd consumes the Child to avoid pid reuse + /// races. Use [`pidfd`] and [`BorrowedFd::try_clone_to_owned`] if + /// you don't want to disassemble the Child yet. + /// /// Even if requested, a pidfd may not be available due to an older /// version of Linux being in use, or if some other error occurred. /// /// [`Command`]: process::Command /// [`create_pidfd`]: CommandExt::create_pidfd + /// [`pidfd`]: ChildExt::pidfd /// [`Child`]: process::Child - fn take_pidfd(&mut self) -> Result; + fn into_pidfd(self) -> crate::result::Result + where + Self: Sized; } /// Os-specific extensions for [`Command`] @@ -146,7 +189,7 @@ pub trait CommandExt: Sealed { /// spawned by this [`Command`]. /// By default, no pidfd will be created. /// - /// The pidfd can be retrieved from the child with [`pidfd`] or [`take_pidfd`]. + /// The pidfd can be retrieved from the child with [`pidfd`] or [`into_pidfd`]. /// /// A pidfd will only be created if it is possible to do so /// in a guaranteed race-free manner. Otherwise, [`pidfd`] will return an error. @@ -160,7 +203,7 @@ pub trait CommandExt: Sealed { /// [`Command`]: process::Command /// [`Child`]: process::Child /// [`pidfd`]: fn@ChildExt::pidfd - /// [`take_pidfd`]: ChildExt::take_pidfd + /// [`into_pidfd`]: ChildExt::into_pidfd fn create_pidfd(&mut self, val: bool) -> &mut process::Command; } diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs index c29dd62bc06f0..d53674d3c5f2c 100644 --- a/library/std/src/os/linux/raw.rs +++ b/library/std/src/os/linux/raw.rs @@ -244,7 +244,11 @@ mod arch { pub use libc::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; } -#[cfg(target_arch = "aarch64")] +#[cfg(any( + target_arch = "aarch64", + // Arm64EC is Windows-only, but docs are always build as Linux, so re-use AArch64 for Arm64EC. + all(doc, target_arch = "arm64ec") +))] mod arch { use crate::os::raw::{c_int, c_long}; diff --git a/library/std/src/os/macos/mod.rs b/library/std/src/os/macos/mod.rs index 791d703b142cf..59fe90834c2b4 100644 --- a/library/std/src/os/macos/mod.rs +++ b/library/std/src/os/macos/mod.rs @@ -2,5 +2,28 @@ #![stable(feature = "raw_ext", since = "1.1.0")] -pub mod fs; -pub mod raw; +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub mod fs { + #[doc(inline)] + #[stable(feature = "file_set_times", since = "1.75.0")] + pub use crate::os::darwin::fs::FileTimesExt; + #[doc(inline)] + #[stable(feature = "metadata_ext", since = "1.1.0")] + pub use crate::os::darwin::fs::MetadataExt; +} + +/// macOS-specific raw type definitions +#[stable(feature = "raw_ext", since = "1.1.0")] +#[deprecated( + since = "1.8.0", + note = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions" +)] +#[allow(deprecated)] +pub mod raw { + #[doc(inline)] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub use crate::os::darwin::raw::*; +} diff --git a/library/std/src/os/macos/raw.rs b/library/std/src/os/macos/raw.rs deleted file mode 100644 index 0b21f6ee5e498..0000000000000 --- a/library/std/src/os/macos/raw.rs +++ /dev/null @@ -1,83 +0,0 @@ -//! macOS-specific raw type definitions - -#![stable(feature = "raw_ext", since = "1.1.0")] -#![deprecated( - since = "1.8.0", - note = "these type aliases are no longer supported by \ - the standard library, the `libc` crate on \ - crates.io should be used instead for the correct \ - definitions" -)] -#![allow(deprecated)] - -use crate::os::raw::c_long; - -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type blkcnt_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type blksize_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type dev_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type ino_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type mode_t = u32; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type nlink_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type off_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type time_t = i64; - -#[stable(feature = "pthread_t", since = "1.8.0")] -pub type pthread_t = usize; - -#[repr(C)] -#[derive(Clone)] -#[stable(feature = "raw_ext", since = "1.1.0")] -pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: u16, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u16, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_birthtime: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_birthtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_flags: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gen: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_lspare: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_qspare: [i64; 2], -} diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index ca3584e82f918..020a8b324f410 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -2,6 +2,7 @@ #![stable(feature = "os", since = "1.0.0")] #![allow(missing_docs, nonstandard_style, missing_debug_implementations)] +#![allow(unsafe_op_in_unsafe_fn)] pub mod raw; @@ -14,7 +15,7 @@ pub mod raw; // documented don't compile (missing things in `libc` which is empty), // so just omit them with an empty module and add the "unstable" attribute. -// Unix, linux, wasi and windows are handled a bit differently. +// unix, linux, wasi and windows are handled a bit differently. #[cfg(all( doc, any( @@ -104,6 +105,8 @@ pub mod windows; pub mod aix; #[cfg(target_os = "android")] pub mod android; +#[cfg(target_vendor = "apple")] +pub(crate) mod darwin; #[cfg(target_os = "dragonfly")] pub mod dragonfly; #[cfg(target_os = "emscripten")] @@ -144,23 +147,16 @@ pub mod redox; pub mod solaris; #[cfg(target_os = "solid_asp3")] pub mod solid; -#[cfg(target_os = "tvos")] -#[path = "ios/mod.rs"] -pub(crate) mod tvos; #[cfg(target_os = "uefi")] pub mod uefi; -#[cfg(target_os = "visionos")] -pub(crate) mod visionos; #[cfg(target_os = "vita")] pub mod vita; #[cfg(target_os = "vxworks")] pub mod vxworks; -#[cfg(target_os = "watchos")] -pub(crate) mod watchos; #[cfg(target_os = "xous")] pub mod xous; -#[cfg(any(unix, target_os = "wasi", doc))] +#[cfg(any(unix, target_os = "hermit", target_os = "wasi", doc))] pub mod fd; #[cfg(any(target_os = "linux", target_os = "android", doc))] diff --git a/library/std/src/os/net/linux_ext/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs index ff29afe7ed311..c8d012962d45a 100644 --- a/library/std/src/os/net/linux_ext/tcp.rs +++ b/library/std/src/os/net/linux_ext/tcp.rs @@ -2,10 +2,9 @@ //! //! [`std::net`]: crate::net -use crate::io; -use crate::net; use crate::sealed::Sealed; use crate::sys_common::AsInner; +use crate::{io, net}; /// Os-specific extensions for [`TcpStream`] /// diff --git a/library/std/src/os/net/linux_ext/tests.rs b/library/std/src/os/net/linux_ext/tests.rs index f8dbbfc18e28e..12f35696abc5c 100644 --- a/library/std/src/os/net/linux_ext/tests.rs +++ b/library/std/src/os/net/linux_ext/tests.rs @@ -1,9 +1,8 @@ #[test] fn quickack() { - use crate::{ - net::{test::next_test_ip4, TcpListener, TcpStream}, - os::net::linux_ext::tcp::TcpStreamExt, - }; + use crate::net::test::next_test_ip4; + use crate::net::{TcpListener, TcpStream}; + use crate::os::net::linux_ext::tcp::TcpStreamExt; macro_rules! t { ($e:expr) => { @@ -30,10 +29,9 @@ fn quickack() { #[test] #[cfg(target_os = "linux")] fn deferaccept() { - use crate::{ - net::{test::next_test_ip4, TcpListener, TcpStream}, - os::net::linux_ext::tcp::TcpStreamExt, - }; + use crate::net::test::next_test_ip4; + use crate::net::{TcpListener, TcpStream}; + use crate::os::net::linux_ext::tcp::TcpStreamExt; macro_rules! t { ($e:expr) => { diff --git a/library/std/src/os/netbsd/fs.rs b/library/std/src/os/netbsd/fs.rs index fe0be069e5e3f..74fbbabb17a5d 100644 --- a/library/std/src/os/netbsd/fs.rs +++ b/library/std/src/os/netbsd/fs.rs @@ -1,10 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; - #[allow(deprecated)] use crate::os::netbsd::raw; +use crate::sys_common::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/netbsd/net.rs b/library/std/src/os/netbsd/net.rs index b9679c7b3af33..e1950d349bf7d 100644 --- a/library/std/src/os/netbsd/net.rs +++ b/library/std/src/os/netbsd/net.rs @@ -42,7 +42,7 @@ pub trait UnixSocketExt: Sealed { #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] fn set_local_creds(&self, local_creds: bool) -> io::Result<()>; - /// Get a filter name if one had been set previously on the socket. + /// Gets a filter name if one had been set previously on the socket. #[unstable(feature = "acceptfilter", issue = "121891")] fn acceptfilter(&self) -> io::Result<&CStr>; diff --git a/library/std/src/os/openbsd/fs.rs b/library/std/src/os/openbsd/fs.rs index b8d8d31c5b8cf..e584098476a7b 100644 --- a/library/std/src/os/openbsd/fs.rs +++ b/library/std/src/os/openbsd/fs.rs @@ -1,10 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; - #[allow(deprecated)] use crate::os::openbsd::raw; +use crate::sys_common::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/redox/fs.rs b/library/std/src/os/redox/fs.rs index 682ca6a2c0309..c6b813f0cc6ce 100644 --- a/library/std/src/os/redox/fs.rs +++ b/library/std/src/os/redox/fs.rs @@ -1,10 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; - #[allow(deprecated)] use crate::os::redox::raw; +use crate::sys_common::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/solaris/fs.rs b/library/std/src/os/solaris/fs.rs index 0931437370429..9b0527d713891 100644 --- a/library/std/src/os/solaris/fs.rs +++ b/library/std/src/os/solaris/fs.rs @@ -1,10 +1,9 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; - #[allow(deprecated)] use crate::os::solaris::raw; +use crate::sys_common::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index 19b4fe22093c3..2d18f33961506 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -44,15 +44,12 @@ //! //! [`BorrowedFd<'a>`]: crate::os::solid::io::BorrowedFd -#![deny(unsafe_op_in_unsafe_fn)] #![unstable(feature = "solid_ext", issue = "none")] -use crate::fmt; use crate::marker::PhantomData; -use crate::mem::forget; -use crate::net; -use crate::sys; +use crate::mem::ManuallyDrop; use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::{fmt, net, sys}; /// Raw file descriptors. pub type RawFd = i32; @@ -99,7 +96,7 @@ pub struct OwnedFd { } impl BorrowedFd<'_> { - /// Return a `BorrowedFd` holding the given raw file descriptor. + /// Returns a `BorrowedFd` holding the given raw file descriptor. /// /// # Safety /// @@ -149,9 +146,7 @@ impl AsRawFd for OwnedFd { impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - let fd = self.fd; - forget(self); - fd + ManuallyDrop::new(self).fd } } @@ -347,6 +342,7 @@ pub trait IntoRawFd { /// This function **transfers ownership** of the underlying file descriptor /// to the caller. Callers are then the unique owners of the file descriptor /// and must close the descriptor once it's no longer needed. + #[must_use = "losing the raw file descriptor may leak resources"] fn into_raw_fd(self) -> RawFd; } diff --git a/library/std/src/os/solid/mod.rs b/library/std/src/os/solid/mod.rs index 0bb83c73ddf7c..75824048e2498 100644 --- a/library/std/src/os/solid/mod.rs +++ b/library/std/src/os/solid/mod.rs @@ -1,4 +1,5 @@ #![stable(feature = "rust1", since = "1.0.0")] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod ffi; pub mod io; diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index 5d082e7c11388..cf8ae697e389d 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -2,8 +2,9 @@ #![unstable(feature = "uefi_std", issue = "100499")] +use crate::ffi::c_void; +use crate::ptr::NonNull; use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; -use crate::{ffi::c_void, ptr::NonNull}; static SYSTEM_TABLE: AtomicPtr = AtomicPtr::new(crate::ptr::null_mut()); static IMAGE_HANDLE: AtomicPtr = AtomicPtr::new(crate::ptr::null_mut()); @@ -26,7 +27,7 @@ static BOOT_SERVICES_FLAG: AtomicBool = AtomicBool::new(false); /// standard library is loaded. /// /// # SAFETY -/// Calling this function more than once will panic +/// Calling this function more than once will panic. pub(crate) unsafe fn init_globals(handle: NonNull, system_table: NonNull) { IMAGE_HANDLE .compare_exchange( @@ -47,23 +48,25 @@ pub(crate) unsafe fn init_globals(handle: NonNull, system_table: NonNull BOOT_SERVICES_FLAG.store(true, Ordering::Release) } -/// Get the SystemTable Pointer. +/// Gets the SystemTable Pointer. +/// /// If you want to use `BootServices` then please use [`boot_services`] as it performs some /// additional checks. /// -/// Note: This function panics if the System Table or Image Handle is not initialized +/// Note: This function panics if the System Table or Image Handle is not initialized. pub fn system_table() -> NonNull { try_system_table().unwrap() } -/// Get the ImageHandle Pointer. +/// Gets the ImageHandle Pointer. /// -/// Note: This function panics if the System Table or Image Handle is not initialized +/// Note: This function panics if the System Table or Image Handle is not initialized. pub fn image_handle() -> NonNull { try_image_handle().unwrap() } -/// Get the BootServices Pointer. +/// Gets the BootServices Pointer. +/// /// This function also checks if `ExitBootServices` has already been called. pub fn boot_services() -> Option> { if BOOT_SERVICES_FLAG.load(Ordering::Acquire) { @@ -75,14 +78,16 @@ pub fn boot_services() -> Option> { } } -/// Get the SystemTable Pointer. -/// This function is mostly intended for places where panic is not an option +/// Gets the SystemTable Pointer. +/// +/// This function is mostly intended for places where panic is not an option. pub(crate) fn try_system_table() -> Option> { NonNull::new(SYSTEM_TABLE.load(Ordering::Acquire)) } -/// Get the SystemHandle Pointer. -/// This function is mostly intended for places where panicking is not an option +/// Gets the SystemHandle Pointer. +/// +/// This function is mostly intended for places where panicking is not an option. pub(crate) fn try_image_handle() -> Option> { NonNull::new(IMAGE_HANDLE.load(Ordering::Acquire)) } diff --git a/library/std/src/os/uefi/mod.rs b/library/std/src/os/uefi/mod.rs index 8ef05eee1f4e7..b42d796b28f69 100644 --- a/library/std/src/os/uefi/mod.rs +++ b/library/std/src/os/uefi/mod.rs @@ -2,6 +2,7 @@ #![unstable(feature = "uefi_std", issue = "100499")] #![doc(cfg(target_os = "uefi"))] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod env; #[path = "../windows/ffi.rs"] diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 970023d8cf19e..caf6980afd91b 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -4,18 +4,18 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[allow(unused_imports)] +use io::{Read, Write}; + use super::platform::fs::MetadataExt as _; +// Used for `File::read` on intra-doc links +use crate::ffi::OsStr; use crate::fs::{self, OpenOptions, Permissions}; -use crate::io; use crate::os::unix::io::{AsFd, AsRawFd}; use crate::path::Path; -use crate::sys; -use crate::sys_common::{AsInner, AsInnerMut, FromInner}; -// Used for `File::read` on intra-doc links -use crate::ffi::OsStr; use crate::sealed::Sealed; -#[allow(unused_imports)] -use io::{Read, Write}; +use crate::sys_common::{AsInner, AsInnerMut, FromInner}; +use crate::{io, sys}; // Tests for this module #[cfg(test)] @@ -1064,7 +1064,7 @@ pub fn lchown>(dir: P, uid: Option, gid: Option) -> io: /// } /// ``` #[stable(feature = "unix_chroot", since = "1.56.0")] -#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] +#[cfg(not(target_os = "fuchsia"))] pub fn chroot>(dir: P) -> io::Result<()> { sys::fs::chroot(dir.as_ref()) } diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index d7a622012a5ac..c6581b9c4c8c8 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -41,6 +41,8 @@ mod platform { pub use crate::os::aix::*; #[cfg(target_os = "android")] pub use crate::os::android::*; + #[cfg(target_vendor = "apple")] + pub(super) use crate::os::darwin::*; #[cfg(target_os = "dragonfly")] pub use crate::os::dragonfly::*; #[cfg(target_os = "emscripten")] @@ -59,14 +61,10 @@ mod platform { pub use crate::os::hurd::*; #[cfg(target_os = "illumos")] pub use crate::os::illumos::*; - #[cfg(target_os = "ios")] - pub use crate::os::ios::*; #[cfg(target_os = "l4re")] pub use crate::os::l4re::*; #[cfg(target_os = "linux")] pub use crate::os::linux::*; - #[cfg(target_os = "macos")] - pub use crate::os::macos::*; #[cfg(target_os = "netbsd")] pub use crate::os::netbsd::*; #[cfg(target_os = "nto")] @@ -77,16 +75,10 @@ mod platform { pub use crate::os::redox::*; #[cfg(target_os = "solaris")] pub use crate::os::solaris::*; - #[cfg(target_os = "tvos")] - pub use crate::os::tvos::*; - #[cfg(target_os = "visionos")] - pub use crate::os::visionos::*; #[cfg(target_os = "vita")] pub use crate::os::vita::*; #[cfg(target_os = "vxworks")] pub use crate::os::vxworks::*; - #[cfg(target_os = "watchos")] - pub use crate::os::watchos::*; } pub mod ffi; diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs index 0597fdcbd7289..9b487a6298247 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary.rs @@ -3,7 +3,7 @@ use super::{sockaddr_un, SocketAddr}; use crate::io::{self, IoSlice, IoSliceMut}; use crate::marker::PhantomData; -use crate::mem::{size_of, zeroed}; +use crate::mem::zeroed; use crate::os::unix::io::RawFd; use crate::path::Path; use crate::ptr::{eq, read_unaligned}; @@ -164,7 +164,7 @@ struct AncillaryDataIter<'a, T> { } impl<'a, T> AncillaryDataIter<'a, T> { - /// Create `AncillaryDataIter` struct to iterate through the data unit in the control message. + /// Creates `AncillaryDataIter` struct to iterate through the data unit in the control message. /// /// # Safety /// @@ -220,7 +220,7 @@ pub struct SocketCred(libc::sockcred2); #[doc(cfg(any(target_os = "android", target_os = "linux")))] #[cfg(any(target_os = "android", target_os = "linux"))] impl SocketCred { - /// Create a Unix credential struct. + /// Creates a Unix credential struct. /// /// PID, UID and GID is set to 0. #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -235,7 +235,7 @@ impl SocketCred { self.0.pid = pid; } - /// Get the current PID. + /// Gets the current PID. #[must_use] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn get_pid(&self) -> libc::pid_t { @@ -248,7 +248,7 @@ impl SocketCred { self.0.uid = uid; } - /// Get the current UID. + /// Gets the current UID. #[must_use] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn get_uid(&self) -> libc::uid_t { @@ -261,7 +261,7 @@ impl SocketCred { self.0.gid = gid; } - /// Get the current GID. + /// Gets the current GID. #[must_use] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn get_gid(&self) -> libc::gid_t { @@ -271,7 +271,7 @@ impl SocketCred { #[cfg(target_os = "freebsd")] impl SocketCred { - /// Create a Unix credential struct. + /// Creates a Unix credential struct. /// /// PID, UID and GID is set to 0. #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -295,7 +295,7 @@ impl SocketCred { self.0.sc_pid = pid; } - /// Get the current PID. + /// Gets the current PID. #[must_use] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn get_pid(&self) -> libc::pid_t { @@ -308,7 +308,7 @@ impl SocketCred { self.0.sc_euid = uid; } - /// Get the current UID. + /// Gets the current UID. #[must_use] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn get_uid(&self) -> libc::uid_t { @@ -321,7 +321,7 @@ impl SocketCred { self.0.sc_egid = gid; } - /// Get the current GID. + /// Gets the current GID. #[must_use] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn get_gid(&self) -> libc::gid_t { @@ -331,7 +331,7 @@ impl SocketCred { #[cfg(target_os = "netbsd")] impl SocketCred { - /// Create a Unix credential struct. + /// Creates a Unix credential struct. /// /// PID, UID and GID is set to 0. #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -353,7 +353,7 @@ impl SocketCred { self.0.sc_pid = pid; } - /// Get the current PID. + /// Gets the current PID. #[must_use] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn get_pid(&self) -> libc::pid_t { @@ -366,7 +366,7 @@ impl SocketCred { self.0.sc_uid = uid; } - /// Get the current UID. + /// Gets the current UID. #[must_use] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn get_uid(&self) -> libc::uid_t { @@ -379,7 +379,7 @@ impl SocketCred { self.0.sc_gid = gid; } - /// Get the current GID. + /// Gets the current GID. #[must_use] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn get_gid(&self) -> libc::gid_t { @@ -466,7 +466,7 @@ pub enum AncillaryData<'a> { } impl<'a> AncillaryData<'a> { - /// Create an `AncillaryData::ScmRights` variant. + /// Creates an `AncillaryData::ScmRights` variant. /// /// # Safety /// @@ -478,7 +478,7 @@ impl<'a> AncillaryData<'a> { AncillaryData::ScmRights(scm_rights) } - /// Create an `AncillaryData::ScmCredentials` variant. + /// Creates an `AncillaryData::ScmCredentials` variant. /// /// # Safety /// @@ -605,7 +605,7 @@ pub struct SocketAncillary<'a> { } impl<'a> SocketAncillary<'a> { - /// Create an ancillary data with the given buffer. + /// Creates an ancillary data with the given buffer. /// /// # Example /// diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index b29f9099a1111..a605c3d4a2602 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -1,3 +1,17 @@ +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "nto", +))] +use libc::MSG_NOSIGNAL; + #[cfg(any(doc, target_os = "android", target_os = "linux"))] use super::{recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to, SocketAncillary}; use super::{sockaddr_un, SocketAddr}; @@ -12,18 +26,6 @@ use crate::sys::net::Socket; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{fmt, io}; - -#[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "openbsd", - target_os = "netbsd", - target_os = "haiku", - target_os = "nto", -))] -use libc::MSG_NOSIGNAL; #[cfg(not(any( target_os = "linux", target_os = "android", @@ -31,6 +33,8 @@ use libc::MSG_NOSIGNAL; target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", + target_os = "solaris", + target_os = "illumos", target_os = "haiku", target_os = "nto", )))] diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index e456e41b21c88..21e2176185d25 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -1,18 +1,16 @@ use super::*; use crate::io::prelude::*; use crate::io::{self, ErrorKind, IoSlice, IoSliceMut}; +#[cfg(target_os = "android")] +use crate::os::android::net::{SocketAddrExt, UnixSocketExt}; +#[cfg(target_os = "linux")] +use crate::os::linux::net::{SocketAddrExt, UnixSocketExt}; #[cfg(any(target_os = "android", target_os = "linux"))] use crate::os::unix::io::AsRawFd; use crate::sys_common::io::test::tmpdir; use crate::thread; use crate::time::Duration; -#[cfg(target_os = "android")] -use crate::os::android::net::{SocketAddrExt, UnixSocketExt}; - -#[cfg(target_os = "linux")] -use crate::os::linux::net::{SocketAddrExt, UnixSocketExt}; - macro_rules! or_panic { ($e:expr) => { match $e { diff --git a/library/std/src/os/unix/net/ucred.rs b/library/std/src/os/unix/net/ucred.rs index 1497e730bbf15..b96e373ad0ae3 100644 --- a/library/std/src/os/unix/net/ucred.rs +++ b/library/std/src/os/unix/net/ucred.rs @@ -23,9 +23,8 @@ pub struct UCred { pub pid: Option, } -#[cfg(any(target_os = "android", target_os = "linux"))] -pub(super) use self::impl_linux::peer_cred; - +#[cfg(target_vendor = "apple")] +pub(super) use self::impl_apple::peer_cred; #[cfg(any( target_os = "dragonfly", target_os = "freebsd", @@ -34,17 +33,17 @@ pub(super) use self::impl_linux::peer_cred; target_os = "nto" ))] pub(super) use self::impl_bsd::peer_cred; - -#[cfg(target_vendor = "apple")] -pub(super) use self::impl_apple::peer_cred; +#[cfg(any(target_os = "android", target_os = "linux"))] +pub(super) use self::impl_linux::peer_cred; #[cfg(any(target_os = "linux", target_os = "android"))] mod impl_linux { + use libc::{c_void, getsockopt, socklen_t, ucred, SOL_SOCKET, SO_PEERCRED}; + use super::UCred; use crate::os::unix::io::AsRawFd; use crate::os::unix::net::UnixStream; use crate::{io, mem}; - use libc::{c_void, getsockopt, socklen_t, ucred, SOL_SOCKET, SO_PEERCRED}; pub fn peer_cred(socket: &UnixStream) -> io::Result { let ucred_size = mem::size_of::(); @@ -99,11 +98,12 @@ mod impl_bsd { #[cfg(target_vendor = "apple")] mod impl_apple { + use libc::{c_void, getpeereid, getsockopt, pid_t, socklen_t, LOCAL_PEERPID, SOL_LOCAL}; + use super::UCred; use crate::os::unix::io::AsRawFd; use crate::os::unix::net::UnixStream; use crate::{io, mem}; - use libc::{c_void, getpeereid, getsockopt, pid_t, socklen_t, LOCAL_PEERPID, SOL_LOCAL}; pub fn peer_cred(socket: &UnixStream) -> io::Result { let mut cred = UCred { uid: 1, gid: 1, pid: None }; diff --git a/library/std/src/os/unix/net/ucred/tests.rs b/library/std/src/os/unix/net/ucred/tests.rs index a6cc81318fc08..59414f4dcae13 100644 --- a/library/std/src/os/unix/net/ucred/tests.rs +++ b/library/std/src/os/unix/net/ucred/tests.rs @@ -1,6 +1,7 @@ -use crate::os::unix::net::UnixStream; use libc::{getegid, geteuid, getpid}; +use crate::os::unix::net::UnixStream; + #[test] #[cfg(any( target_os = "android", diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 72ea54bd77203..c53423675bd00 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -4,15 +4,13 @@ #![stable(feature = "rust1", since = "1.0.0")] +use cfg_if::cfg_if; + use crate::ffi::OsStr; -use crate::io; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::process; use crate::sealed::Sealed; -use crate::sys; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; - -use cfg_if::cfg_if; +use crate::{io, process, sys}; cfg_if! { if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { @@ -444,7 +442,7 @@ impl From for OwnedFd { } } -/// Create a `ChildStdin` from the provided `OwnedFd`. +/// Creates a `ChildStdin` from the provided `OwnedFd`. /// /// The provided file descriptor must point to a pipe /// with the `CLOEXEC` flag set. @@ -475,7 +473,7 @@ impl From for OwnedFd { } } -/// Create a `ChildStdout` from the provided `OwnedFd`. +/// Creates a `ChildStdout` from the provided `OwnedFd`. /// /// The provided file descriptor must point to a pipe /// with the `CLOEXEC` flag set. @@ -506,7 +504,7 @@ impl From for OwnedFd { } } -/// Create a `ChildStderr` from the provided `OwnedFd`. +/// Creates a `ChildStderr` from the provided `OwnedFd`. /// /// The provided file descriptor must point to a pipe /// with the `CLOEXEC` flag set. diff --git a/library/std/src/os/visionos/fs.rs b/library/std/src/os/visionos/fs.rs deleted file mode 100644 index e5df4de0b7f71..0000000000000 --- a/library/std/src/os/visionos/fs.rs +++ /dev/null @@ -1,160 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::{self, Metadata}; -use crate::sealed::Sealed; -use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; -use crate::time::SystemTime; - -#[allow(deprecated)] -use super::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: crate::fs::Metadata -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[deprecated( - since = "1.8.0", - note = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blocks(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_flags(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gen(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_lspare(&self) -> u32; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for Metadata { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_birthtime(&self) -> i64 { - self.as_inner().as_inner().st_birthtime as i64 - } - fn st_birthtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_birthtime_nsec as i64 - } - fn st_blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn st_blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } - fn st_gen(&self) -> u32 { - self.as_inner().as_inner().st_gen as u32 - } - fn st_flags(&self) -> u32 { - self.as_inner().as_inner().st_flags as u32 - } - fn st_lspare(&self) -> u32 { - self.as_inner().as_inner().st_lspare as u32 - } -} - -/// OS-specific extensions to [`fs::FileTimes`]. -#[stable(feature = "file_set_times", since = "1.75.0")] -pub trait FileTimesExt: Sealed { - /// Set the creation time of a file. - #[stable(feature = "file_set_times", since = "1.75.0")] - fn set_created(self, t: SystemTime) -> Self; -} - -#[stable(feature = "file_set_times", since = "1.75.0")] -impl FileTimesExt for fs::FileTimes { - fn set_created(mut self, t: SystemTime) -> Self { - self.as_inner_mut().set_created(t.into_inner()); - self - } -} diff --git a/library/std/src/os/visionos/mod.rs b/library/std/src/os/visionos/mod.rs deleted file mode 100644 index f4b061ffda898..0000000000000 --- a/library/std/src/os/visionos/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! visionos-specific definitions - -#![stable(feature = "raw_ext", since = "1.1.0")] - -pub mod fs; -pub mod raw; diff --git a/library/std/src/os/visionos/raw.rs b/library/std/src/os/visionos/raw.rs deleted file mode 100644 index 2b3eca6f493df..0000000000000 --- a/library/std/src/os/visionos/raw.rs +++ /dev/null @@ -1,83 +0,0 @@ -//! visionos-specific raw type definitions - -#![stable(feature = "raw_ext", since = "1.1.0")] -#![deprecated( - since = "1.8.0", - note = "these type aliases are no longer supported by \ - the standard library, the `libc` crate on \ - crates.io should be used instead for the correct \ - definitions" -)] -#![allow(deprecated)] - -use crate::os::raw::c_long; - -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type blkcnt_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type blksize_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type dev_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type ino_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type mode_t = u32; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type nlink_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type off_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type time_t = i64; - -#[stable(feature = "pthread_t", since = "1.8.0")] -pub type pthread_t = usize; - -#[repr(C)] -#[derive(Clone)] -#[stable(feature = "raw_ext", since = "1.1.0")] -pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: u16, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u16, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_birthtime: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_birthtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_flags: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gen: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_lspare: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_qspare: [i64; 2], -} diff --git a/library/std/src/os/vita/mod.rs b/library/std/src/os/vita/mod.rs index da9edd12f7b03..92a2bf0a59ab5 100644 --- a/library/std/src/os/vita/mod.rs +++ b/library/std/src/os/vita/mod.rs @@ -1,5 +1,6 @@ //! Definitions for vita +#![forbid(unsafe_op_in_unsafe_fn)] #![stable(feature = "raw_ext", since = "1.1.0")] pub mod fs; diff --git a/library/std/src/os/vita/raw.rs b/library/std/src/os/vita/raw.rs index 74cae4d4135d1..e4b65ef1ed767 100644 --- a/library/std/src/os/vita/raw.rs +++ b/library/std/src/os/vita/raw.rs @@ -10,9 +10,6 @@ )] #![allow(deprecated)] -use crate::os::raw::c_long; -use crate::os::unix::raw::{gid_t, uid_t}; - #[stable(feature = "pthread_t", since = "1.8.0")] pub type pthread_t = libc::pthread_t; @@ -34,37 +31,3 @@ pub type off_t = libc::off_t; #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = libc::time_t; - -#[repr(C)] -#[derive(Clone)] -#[stable(feature = "raw_ext", since = "1.1.0")] -pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: dev_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: ino_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: mode_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: nlink_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: uid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: gid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: dev_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: off_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: blksize_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: blkcnt_t, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_spare4: [c_long; 2usize], -} diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 46fc2a50de911..a58ca543d6777 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -5,14 +5,15 @@ #![deny(unsafe_op_in_unsafe_fn)] #![unstable(feature = "wasi_ext", issue = "71213")] +// Used for `File::read` on intra-doc links +#[allow(unused_imports)] +use io::{Read, Write}; + use crate::ffi::OsStr; use crate::fs::{self, File, Metadata, OpenOptions}; use crate::io::{self, IoSlice, IoSliceMut}; use crate::path::{Path, PathBuf}; use crate::sys_common::{AsInner, AsInnerMut, FromInner}; -// Used for `File::read` on intra-doc links -#[allow(unused_imports)] -use io::{Read, Write}; /// WASI-specific extensions to [`File`]. pub trait FileExt { @@ -169,55 +170,55 @@ pub trait FileExt { #[doc(alias = "fd_tell")] fn tell(&self) -> io::Result; - /// Adjust the flags associated with this file. + /// Adjusts the flags associated with this file. /// /// This corresponds to the `fd_fdstat_set_flags` syscall. #[doc(alias = "fd_fdstat_set_flags")] fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>; - /// Adjust the rights associated with this file. + /// Adjusts the rights associated with this file. /// /// This corresponds to the `fd_fdstat_set_rights` syscall. #[doc(alias = "fd_fdstat_set_rights")] fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>; - /// Provide file advisory information on a file descriptor. + /// Provides file advisory information on a file descriptor. /// /// This corresponds to the `fd_advise` syscall. #[doc(alias = "fd_advise")] fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>; - /// Force the allocation of space in a file. + /// Forces the allocation of space in a file. /// /// This corresponds to the `fd_allocate` syscall. #[doc(alias = "fd_allocate")] fn allocate(&self, offset: u64, len: u64) -> io::Result<()>; - /// Create a directory. + /// Creates a directory. /// /// This corresponds to the `path_create_directory` syscall. #[doc(alias = "path_create_directory")] fn create_directory>(&self, dir: P) -> io::Result<()>; - /// Read the contents of a symbolic link. + /// Reads the contents of a symbolic link. /// /// This corresponds to the `path_readlink` syscall. #[doc(alias = "path_readlink")] fn read_link>(&self, path: P) -> io::Result; - /// Return the attributes of a file or directory. + /// Returns the attributes of a file or directory. /// /// This corresponds to the `path_filestat_get` syscall. #[doc(alias = "path_filestat_get")] fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result; - /// Unlink a file. + /// Unlinks a file. /// /// This corresponds to the `path_unlink_file` syscall. #[doc(alias = "path_unlink_file")] fn remove_file>(&self, path: P) -> io::Result<()>; - /// Remove a directory. + /// Removes a directory. /// /// This corresponds to the `path_remove_directory` syscall. #[doc(alias = "path_remove_directory")] @@ -501,7 +502,7 @@ impl DirEntryExt for fs::DirEntry { } } -/// Create a hard link. +/// Creates a hard link. /// /// This corresponds to the `path_link` syscall. #[doc(alias = "path_link")] @@ -520,7 +521,7 @@ pub fn link, U: AsRef>( ) } -/// Rename a file or directory. +/// Renames a file or directory. /// /// This corresponds to the `path_rename` syscall. #[doc(alias = "path_rename")] @@ -537,7 +538,7 @@ pub fn rename, U: AsRef>( ) } -/// Create a symbolic link. +/// Creates a symbolic link. /// /// This corresponds to the `path_symlink` syscall. #[doc(alias = "path_symlink")] @@ -551,7 +552,7 @@ pub fn symlink, U: AsRef>( .symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?) } -/// Create a symbolic link. +/// Creates a symbolic link. /// /// This is a convenience API similar to `std::os::unix::fs::symlink` and /// `std::os::windows::fs::symlink_file` and `std::os::windows::fs::symlink_dir`. diff --git a/library/std/src/os/wasi/net/mod.rs b/library/std/src/os/wasi/net/mod.rs index 73c097d4a50ab..4704dd574517a 100644 --- a/library/std/src/os/wasi/net/mod.rs +++ b/library/std/src/os/wasi/net/mod.rs @@ -2,9 +2,8 @@ #![unstable(feature = "wasi_ext", issue = "71213")] -use crate::io; -use crate::net; use crate::sys_common::AsInner; +use crate::{io, net}; /// WASI-specific extensions to [`std::net::TcpListener`]. /// diff --git a/library/std/src/os/watchos/fs.rs b/library/std/src/os/watchos/fs.rs deleted file mode 100644 index ee215dd598441..0000000000000 --- a/library/std/src/os/watchos/fs.rs +++ /dev/null @@ -1,160 +0,0 @@ -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use crate::fs::{self, Metadata}; -use crate::sealed::Sealed; -use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; -use crate::time::SystemTime; - -#[allow(deprecated)] -use crate::os::watchos::raw; - -/// OS-specific extensions to [`fs::Metadata`]. -/// -/// [`fs::Metadata`]: crate::fs::Metadata -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[deprecated( - since = "1.8.0", - note = "deprecated in favor of the accessor \ - methods of this trait" - )] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_birthtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blocks(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_flags(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gen(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_lspare(&self) -> u32; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for Metadata { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_birthtime(&self) -> i64 { - self.as_inner().as_inner().st_birthtime as i64 - } - fn st_birthtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_birthtime_nsec as i64 - } - fn st_blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn st_blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } - fn st_gen(&self) -> u32 { - self.as_inner().as_inner().st_gen as u32 - } - fn st_flags(&self) -> u32 { - self.as_inner().as_inner().st_flags as u32 - } - fn st_lspare(&self) -> u32 { - self.as_inner().as_inner().st_lspare as u32 - } -} - -/// OS-specific extensions to [`fs::FileTimes`]. -#[stable(feature = "file_set_times", since = "1.75.0")] -pub trait FileTimesExt: Sealed { - /// Set the creation time of a file. - #[stable(feature = "file_set_times", since = "1.75.0")] - fn set_created(self, t: SystemTime) -> Self; -} - -#[stable(feature = "file_set_times", since = "1.75.0")] -impl FileTimesExt for fs::FileTimes { - fn set_created(mut self, t: SystemTime) -> Self { - self.as_inner_mut().set_created(t.into_inner()); - self - } -} diff --git a/library/std/src/os/watchos/mod.rs b/library/std/src/os/watchos/mod.rs deleted file mode 100644 index cd6454ebbf99b..0000000000000 --- a/library/std/src/os/watchos/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! watchOS-specific definitions - -#![stable(feature = "raw_ext", since = "1.1.0")] - -pub mod fs; -pub mod raw; diff --git a/library/std/src/os/watchos/raw.rs b/library/std/src/os/watchos/raw.rs deleted file mode 100644 index 630a533d9aaf2..0000000000000 --- a/library/std/src/os/watchos/raw.rs +++ /dev/null @@ -1,83 +0,0 @@ -//! watchOS-specific raw type definitions - -#![stable(feature = "raw_ext", since = "1.1.0")] -#![deprecated( - since = "1.8.0", - note = "these type aliases are no longer supported by \ - the standard library, the `libc` crate on \ - crates.io should be used instead for the correct \ - definitions" -)] -#![allow(deprecated)] - -use crate::os::raw::c_long; - -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type blkcnt_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type blksize_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type dev_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type ino_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type mode_t = u32; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type nlink_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type off_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] -pub type time_t = i64; - -#[stable(feature = "pthread_t", since = "1.8.0")] -pub type pthread_t = usize; - -#[repr(C)] -#[derive(Clone)] -#[stable(feature = "raw_ext", since = "1.1.0")] -pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_dev: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mode: u16, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_nlink: u16, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: u64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_uid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gid: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_rdev: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_birthtime: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_birthtime_nsec: c_long, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_flags: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_gen: u32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_lspare: i32, - #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_qspare: [i64; 2], -} diff --git a/library/std/src/os/windows/ffi.rs b/library/std/src/os/windows/ffi.rs index 96bab59d3f8d7..496443dbbc3ac 100644 --- a/library/std/src/os/windows/ffi.rs +++ b/library/std/src/os/windows/ffi.rs @@ -56,11 +56,10 @@ use crate::ffi::{OsStr, OsString}; use crate::sealed::Sealed; use crate::sys::os_str::Buf; -use crate::sys_common::wtf8::Wtf8Buf; -use crate::sys_common::{AsInner, FromInner}; - #[stable(feature = "rust1", since = "1.0.0")] pub use crate::sys_common::wtf8::EncodeWide; +use crate::sys_common::wtf8::Wtf8Buf; +use crate::sys_common::{AsInner, FromInner}; /// Windows-specific extensions to [`OsString`]. /// diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index 27947d91c99de..3dcde43cfec78 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -5,12 +5,11 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::fs::{self, Metadata, OpenOptions}; -use crate::io; use crate::path::Path; use crate::sealed::Sealed; -use crate::sys; use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; use crate::time::SystemTime; +use crate::{io, sys}; /// Windows-specific extensions to [`fs::File`]. #[stable(feature = "file_offset", since = "1.15.0")] @@ -471,6 +470,13 @@ pub trait MetadataExt { /// `fs::metadata` or `File::metadata`, then this will return `Some`. #[unstable(feature = "windows_by_handle", issue = "63010")] fn file_index(&self) -> Option; + + /// Returns the change time, which is the last time file metadata was changed, such as + /// renames, attributes, etc + /// + /// This will return `None` if the `Metadata` instance was not created using the `FILE_BASIC_INFO` type. + #[unstable(feature = "windows_change_time", issue = "121478")] + fn change_time(&self) -> Option; } #[stable(feature = "metadata_ext", since = "1.1.0")] @@ -499,6 +505,9 @@ impl MetadataExt for Metadata { fn file_index(&self) -> Option { self.as_inner().file_index() } + fn change_time(&self) -> Option { + self.as_inner().changed_u64() + } } /// Windows-specific extensions to [`fs::FileType`]. @@ -621,7 +630,7 @@ pub fn symlink_dir, Q: AsRef>(original: P, link: Q) -> io:: sys::fs::symlink_inner(original.as_ref(), link.as_ref(), true) } -/// Create a junction point. +/// Creates a junction point. /// /// The `link` path will be a directory junction pointing to the original path. /// If `link` is a relative path then it will be made absolute prior to creating the junction point. diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index a9d1983dce610..a4fa94e2b96a4 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -3,15 +3,11 @@ #![stable(feature = "io_safety", since = "1.63.0")] use super::raw::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; -use crate::fmt; -use crate::fs; -use crate::io; use crate::marker::PhantomData; -use crate::mem::{forget, ManuallyDrop}; -use crate::ptr; -use crate::sys; +use crate::mem::ManuallyDrop; use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::{fmt, fs, io, ptr, sys}; /// A borrowed handle. /// @@ -135,7 +131,7 @@ unsafe impl Sync for HandleOrInvalid {} unsafe impl Sync for BorrowedHandle<'_> {} impl BorrowedHandle<'_> { - /// Return a `BorrowedHandle` holding the given raw handle. + /// Returns a `BorrowedHandle` holding the given raw handle. /// /// # Safety /// @@ -319,9 +315,7 @@ impl AsRawHandle for OwnedHandle { impl IntoRawHandle for OwnedHandle { #[inline] fn into_raw_handle(self) -> RawHandle { - let handle = self.handle; - forget(self); - handle + ManuallyDrop::new(self).handle } } diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 770583a9ce3e0..6658248d574c9 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -2,16 +2,12 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::fs; -use crate::io; -use crate::net; #[cfg(doc)] use crate::os::windows::io::{AsHandle, AsSocket}; use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; -use crate::ptr; -use crate::sys; use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::{fs, io, net, ptr, sys}; /// Raw HANDLEs. #[stable(feature = "rust1", since = "1.0.0")] @@ -44,7 +40,7 @@ pub trait AsRawHandle { fn as_raw_handle(&self) -> RawHandle; } -/// Construct I/O objects from raw handles. +/// Constructs I/O objects from raw handles. #[stable(feature = "from_raw_os", since = "1.1.0")] pub trait FromRawHandle { /// Constructs a new I/O object from the specified raw handle. @@ -89,6 +85,7 @@ pub trait IntoRawHandle { /// However, transferring ownership is not strictly required. Use a /// `Into::into` implementation for an API which strictly /// transfers ownership. + #[must_use = "losing the raw handle may leak resources"] #[stable(feature = "into_raw_os", since = "1.4.0")] fn into_raw_handle(self) -> RawHandle; } @@ -159,10 +156,12 @@ fn stdio_handle(raw: RawHandle) -> RawHandle { impl FromRawHandle for fs::File { #[inline] unsafe fn from_raw_handle(handle: RawHandle) -> fs::File { - let handle = handle as sys::c::HANDLE; - fs::File::from_inner(sys::fs::File::from_inner(FromInner::from_inner( - OwnedHandle::from_raw_handle(handle), - ))) + unsafe { + let handle = handle as sys::c::HANDLE; + fs::File::from_inner(sys::fs::File::from_inner(FromInner::from_inner( + OwnedHandle::from_raw_handle(handle), + ))) + } } } @@ -230,6 +229,7 @@ pub trait IntoRawSocket { /// However, transferring ownership is not strictly required. Use a /// `Into::into` implementation for an API which strictly /// transfers ownership. + #[must_use = "losing the raw socket may leak resources"] #[stable(feature = "into_raw_os", since = "1.4.0")] fn into_raw_socket(self) -> RawSocket; } @@ -260,24 +260,30 @@ impl AsRawSocket for net::UdpSocket { impl FromRawSocket for net::TcpStream { #[inline] unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { - let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock)) + unsafe { + let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); + net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock)) + } } } #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawSocket for net::TcpListener { #[inline] unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { - let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock)) + unsafe { + let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); + net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock)) + } } } #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawSocket for net::UdpSocket { #[inline] unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { - let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock)) + unsafe { + let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); + net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock)) + } } } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 6ffdf907c8ed3..1fcfb6e73ad03 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -3,14 +3,11 @@ #![stable(feature = "io_safety", since = "1.63.0")] use super::raw::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; -use crate::fmt; -use crate::io; use crate::marker::PhantomData; -use crate::mem; -use crate::mem::forget; -use crate::sys; +use crate::mem::{self, ManuallyDrop}; #[cfg(not(target_vendor = "uwp"))] use crate::sys::cvt; +use crate::{fmt, io, sys}; /// A borrowed socket. /// @@ -64,7 +61,7 @@ pub struct OwnedSocket { } impl BorrowedSocket<'_> { - /// Return a `BorrowedSocket` holding the given raw socket. + /// Returns a `BorrowedSocket` holding the given raw socket. /// /// # Safety /// @@ -76,7 +73,7 @@ impl BorrowedSocket<'_> { #[stable(feature = "io_safety", since = "1.63.0")] pub const unsafe fn borrow_raw(socket: RawSocket) -> Self { assert!(socket != sys::c::INVALID_SOCKET as RawSocket); - Self { socket, _phantom: PhantomData } + unsafe { Self { socket, _phantom: PhantomData } } } } @@ -191,9 +188,7 @@ impl AsRawSocket for OwnedSocket { impl IntoRawSocket for OwnedSocket { #[inline] fn into_raw_socket(self) -> RawSocket { - let socket = self.socket; - forget(self); - socket + ManuallyDrop::new(self).socket } } @@ -201,8 +196,10 @@ impl IntoRawSocket for OwnedSocket { impl FromRawSocket for OwnedSocket { #[inline] unsafe fn from_raw_socket(socket: RawSocket) -> Self { - debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket); - Self { socket } + unsafe { + debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket); + Self { socket } + } } } diff --git a/library/std/src/os/windows/mod.rs b/library/std/src/os/windows/mod.rs index 52eb3b7c06769..f452403ee8426 100644 --- a/library/std/src/os/windows/mod.rs +++ b/library/std/src/os/windows/mod.rs @@ -24,6 +24,7 @@ #![stable(feature = "rust1", since = "1.0.0")] #![doc(cfg(windows))] +#![deny(unsafe_op_in_unsafe_fn)] pub mod ffi; pub mod fs; diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 9cca27fa5dd5b..c2830d2eb61d1 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -8,15 +8,14 @@ use crate::ffi::OsStr; use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, }; -use crate::process; use crate::sealed::Sealed; -use crate::sys; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::{process, sys}; #[stable(feature = "process_extensions", since = "1.2.0")] impl FromRawHandle for process::Stdio { unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio { - let handle = sys::handle::Handle::from_raw_handle(handle as *mut _); + let handle = unsafe { sys::handle::Handle::from_raw_handle(handle as *mut _) }; let io = sys::process::Stdio::Handle(handle); process::Stdio::from_inner(io) } @@ -109,7 +108,7 @@ impl IntoRawHandle for process::ChildStderr { } } -/// Create a `ChildStdin` from the provided `OwnedHandle`. +/// Creates a `ChildStdin` from the provided `OwnedHandle`. /// /// The provided handle must be asynchronous, as reading and /// writing from and to it is implemented using asynchronous APIs. @@ -122,7 +121,7 @@ impl From for process::ChildStdin { } } -/// Create a `ChildStdout` from the provided `OwnedHandle`. +/// Creates a `ChildStdout` from the provided `OwnedHandle`. /// /// The provided handle must be asynchronous, as reading and /// writing from and to it is implemented using asynchronous APIs. @@ -135,7 +134,7 @@ impl From for process::ChildStdout { } } -/// Create a `ChildStderr` from the provided `OwnedHandle`. +/// Creates a `ChildStderr` from the provided `OwnedHandle`. /// /// The provided handle must be asynchronous, as reading and /// writing from and to it is implemented using asynchronous APIs. @@ -181,6 +180,14 @@ pub trait CommandExt: Sealed { #[stable(feature = "windows_process_extensions", since = "1.16.0")] fn creation_flags(&mut self, flags: u32) -> &mut process::Command; + /// Sets the field `wShowWindow` of [STARTUPINFO][1] that is passed to `CreateProcess`. + /// Allowed values are the ones listed in + /// + /// + /// [1]: + #[unstable(feature = "windows_process_extensions_show_window", issue = "127544")] + fn show_window(&mut self, cmd_show: u16) -> &mut process::Command; + /// Forces all arguments to be wrapped in quote (`"`) characters. /// /// This is useful for passing arguments to [MSYS2/Cygwin][1] based @@ -370,6 +377,11 @@ impl CommandExt for process::Command { self } + fn show_window(&mut self, cmd_show: u16) -> &mut process::Command { + self.as_inner_mut().show_window(Some(cmd_show)); + self + } + fn force_quotes(&mut self, enabled: bool) -> &mut process::Command { self.as_inner_mut().force_quotes(enabled); self @@ -394,7 +406,7 @@ impl CommandExt for process::Command { attribute: usize, value: T, ) -> &mut process::Command { - self.as_inner_mut().raw_attribute(attribute, value); + unsafe { self.as_inner_mut().raw_attribute(attribute, value) }; self } } diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs index e9a9f53372026..1a4a940bf358a 100644 --- a/library/std/src/os/xous/ffi.rs +++ b/library/std/src/os/xous/ffi.rs @@ -274,7 +274,7 @@ fn connect_impl(address: ServerAddress, blocking: bool) -> Result Result { connect_impl(address, true) } -/// Attempt to connect to a Xous server represented by the specified `address`. +/// Attempts to connect to a Xous server represented by the specified `address`. /// /// If the server does not exist then None is returned. pub(crate) fn try_connect(address: ServerAddress) -> Result, Error> { @@ -293,7 +293,7 @@ pub(crate) fn try_connect(address: ServerAddress) -> Result, } } -/// Terminate the current process and return the specified code to the parent process. +/// Terminates the current process and returns the specified code to the parent process. pub(crate) fn exit(return_code: u32) -> ! { let a0 = Syscall::TerminateProcess as usize; let a1 = return_code as usize; @@ -320,7 +320,7 @@ pub(crate) fn exit(return_code: u32) -> ! { unreachable!(); } -/// Suspend the current thread and allow another thread to run. This thread may +/// Suspends the current thread and allow another thread to run. This thread may /// continue executing again immediately if there are no other threads available /// to run on the system. pub(crate) fn do_yield() { @@ -348,9 +348,11 @@ pub(crate) fn do_yield() { }; } -/// Allocate memory from the system. An optional physical and/or virtual address -/// may be specified in order to ensure memory is allocated at specific offsets, -/// otherwise the kernel will select an address. +/// Allocates memory from the system. +/// +/// An optional physical and/or virtual address may be specified in order to +/// ensure memory is allocated at specific offsets, otherwise the kernel will +/// select an address. /// /// # Safety /// @@ -400,7 +402,7 @@ pub(crate) unsafe fn map_memory( } } -/// Destroy the given memory, returning it to the compiler. +/// Destroys the given memory, returning it to the compiler. /// /// Safety: The memory pointed to by `range` should not be used after this /// function returns, even if this function returns Err(). @@ -439,9 +441,10 @@ pub(crate) unsafe fn unmap_memory(range: *mut [T]) -> Result<(), Error> { } } -/// Adjust the memory flags for the given range. This can be used to remove flags -/// from a given region in order to harden memory access. Note that flags may -/// only be removed and may never be added. +/// Adjusts the memory flags for the given range. +/// +/// This can be used to remove flags from a given region in order to harden +/// memory access. Note that flags may only be removed and may never be added. /// /// Safety: The memory pointed to by `range` may become inaccessible or have its /// mutability removed. It is up to the caller to ensure that the flags specified @@ -484,7 +487,7 @@ pub(crate) unsafe fn update_memory_flags( } } -/// Create a thread with a given stack and up to four arguments +/// Creates a thread with a given stack and up to four arguments. pub(crate) fn create_thread( start: *mut usize, stack: *mut [u8], @@ -527,7 +530,7 @@ pub(crate) fn create_thread( } } -/// Wait for the given thread to terminate and return the exit code from that thread. +/// Waits for the given thread to terminate and returns the exit code from that thread. pub(crate) fn join_thread(thread_id: ThreadId) -> Result { let mut a0 = Syscall::JoinThread as usize; let mut a1 = thread_id.into(); @@ -567,7 +570,7 @@ pub(crate) fn join_thread(thread_id: ThreadId) -> Result { } } -/// Get the current thread's ID +/// Gets the current thread's ID. pub(crate) fn thread_id() -> Result { let mut a0 = Syscall::GetThreadId as usize; let mut a1 = 0; @@ -603,7 +606,7 @@ pub(crate) fn thread_id() -> Result { } } -/// Adjust the given `knob` limit to match the new value `new`. The current value must +/// Adjusts the given `knob` limit to match the new value `new`. The current value must /// match the `current` in order for this to take effect. /// /// The new value is returned as a result of this call. If the call fails, then the old diff --git a/library/std/src/os/xous/mod.rs b/library/std/src/os/xous/mod.rs index 153694a89a78d..4b21695c4ac7e 100644 --- a/library/std/src/os/xous/mod.rs +++ b/library/std/src/os/xous/mod.rs @@ -1,5 +1,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![doc(cfg(target_os = "xous"))] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod ffi; diff --git a/library/std/src/os/xous/services.rs b/library/std/src/os/xous/services.rs index a75be1b857003..ddf0236f5ad74 100644 --- a/library/std/src/os/xous/services.rs +++ b/library/std/src/os/xous/services.rs @@ -1,6 +1,7 @@ -use crate::os::xous::ffi::Connection; use core::sync::atomic::{AtomicU32, Ordering}; +use crate::os::xous::ffi::Connection; + mod dns; pub(crate) use dns::*; @@ -83,7 +84,7 @@ mod ns { } } -/// Attempt to connect to a server by name. If the server does not exist, this will +/// Attempts to connect to a server by name. If the server does not exist, this will /// block until the server is created. /// /// Note that this is different from connecting to a server by address. Server @@ -94,7 +95,7 @@ pub fn connect(name: &str) -> Option { ns::connect_with_name(name) } -/// Attempt to connect to a server by name. If the server does not exist, this will +/// Attempts to connect to a server by name. If the server does not exist, this will /// immediately return `None`. /// /// Note that this is different from connecting to a server by address. Server @@ -107,7 +108,7 @@ pub fn try_connect(name: &str) -> Option { static NAME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); -/// Return a `Connection` to the name server. If the name server has not been started, +/// Returns a `Connection` to the name server. If the name server has not been started, /// then this call will block until the name server has been started. The `Connection` /// will be shared among all connections in a process, so it is safe to call this /// multiple times. diff --git a/library/std/src/os/xous/services/dns.rs b/library/std/src/os/xous/services/dns.rs index a7d88f4892cda..0288164839360 100644 --- a/library/std/src/os/xous/services/dns.rs +++ b/library/std/src/os/xous/services/dns.rs @@ -1,6 +1,7 @@ +use core::sync::atomic::{AtomicU32, Ordering}; + use crate::os::xous::ffi::Connection; use crate::os::xous::services::connect; -use core::sync::atomic::{AtomicU32, Ordering}; #[repr(usize)] pub(crate) enum DnsLendMut { @@ -13,7 +14,7 @@ impl Into for DnsLendMut { } } -/// Return a `Connection` to the DNS lookup server. This server is used for +/// Returns a `Connection` to the DNS lookup server. This server is used for /// querying domain name values. pub(crate) fn dns_server() -> Connection { static DNS_CONNECTION: AtomicU32 = AtomicU32::new(0); diff --git a/library/std/src/os/xous/services/log.rs b/library/std/src/os/xous/services/log.rs index 55a501dc7d00e..1661011ca64b1 100644 --- a/library/std/src/os/xous/services/log.rs +++ b/library/std/src/os/xous/services/log.rs @@ -1,10 +1,11 @@ -use crate::os::xous::ffi::Connection; use core::sync::atomic::{AtomicU32, Ordering}; -/// Group `usize` bytes into a `usize` and return it, beginning -/// from `offset` * sizeof(usize) bytes from the start. For example, -/// `group_or_null([1,2,3,4,5,6,7,8], 1)` on a 32-bit system will -/// return a usize with 5678 packed into it. +use crate::os::xous::ffi::Connection; + +/// Group a `usize` worth of bytes into a `usize` and return it, beginning from +/// `offset` * sizeof(usize) bytes from the start. For example, +/// `group_or_null([1,2,3,4,5,6,7,8], 1)` on a 32-bit system will return a +/// `usize` with 5678 packed into it. fn group_or_null(data: &[u8], offset: usize) -> usize { let start = offset * core::mem::size_of::(); let mut out_array = [0u8; core::mem::size_of::()]; @@ -56,10 +57,12 @@ impl Into for LogLend { } } -/// Return a `Connection` to the log server, which is used for printing messages to -/// the console and reporting panics. If the log server has not yet started, this -/// will block until the server is running. It is safe to call this multiple times, -/// because the address is shared among all threads in a process. +/// Returns a `Connection` to the log server, which is used for printing messages to +/// the console and reporting panics. +/// +/// If the log server has not yet started, this will block until the server is +/// running. It is safe to call this multiple times, because the address is +/// shared among all threads in a process. pub(crate) fn log_server() -> Connection { static LOG_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); diff --git a/library/std/src/os/xous/services/net.rs b/library/std/src/os/xous/services/net.rs index 26d337dcef168..83acc7961b377 100644 --- a/library/std/src/os/xous/services/net.rs +++ b/library/std/src/os/xous/services/net.rs @@ -1,6 +1,7 @@ +use core::sync::atomic::{AtomicU32, Ordering}; + use crate::os::xous::ffi::Connection; use crate::os::xous::services::connect; -use core::sync::atomic::{AtomicU32, Ordering}; pub(crate) enum NetBlockingScalar { StdGetTtlUdp(u16 /* fd */), /* 36 */ @@ -80,7 +81,7 @@ impl<'a> Into<[usize; 5]> for NetBlockingScalar { } } -/// Return a `Connection` to the Network server. This server provides all +/// Returns a `Connection` to the Network server. This server provides all /// OS-level networking functions. pub(crate) fn net_server() -> Connection { static NET_CONNECTION: AtomicU32 = AtomicU32::new(0); diff --git a/library/std/src/os/xous/services/systime.rs b/library/std/src/os/xous/services/systime.rs index bbb875c69426e..079ede7aa86c7 100644 --- a/library/std/src/os/xous/services/systime.rs +++ b/library/std/src/os/xous/services/systime.rs @@ -1,6 +1,7 @@ -use crate::os::xous::ffi::{connect, Connection}; use core::sync::atomic::{AtomicU32, Ordering}; +use crate::os::xous::ffi::{connect, Connection}; + pub(crate) enum SystimeScalar { GetUtcTimeMs, } @@ -13,7 +14,7 @@ impl Into<[usize; 5]> for SystimeScalar { } } -/// Return a `Connection` to the systime server. This server is used for reporting the +/// Returns a `Connection` to the systime server. This server is used for reporting the /// realtime clock. pub(crate) fn systime_server() -> Connection { static SYSTIME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); diff --git a/library/std/src/os/xous/services/ticktimer.rs b/library/std/src/os/xous/services/ticktimer.rs index 7759303fdbe23..66ade6da65cd3 100644 --- a/library/std/src/os/xous/services/ticktimer.rs +++ b/library/std/src/os/xous/services/ticktimer.rs @@ -1,6 +1,7 @@ -use crate::os::xous::ffi::Connection; use core::sync::atomic::{AtomicU32, Ordering}; +use crate::os::xous::ffi::Connection; + pub(crate) enum TicktimerScalar { ElapsedMs, SleepMs(usize), @@ -27,7 +28,7 @@ impl Into<[usize; 5]> for TicktimerScalar { } } -/// Return a `Connection` to the ticktimer server. This server is used for synchronization +/// Returns a `Connection` to the ticktimer server. This server is used for synchronization /// primitives such as sleep, Mutex, and Condvar. pub(crate) fn ticktimer_server() -> Connection { static TICKTIMER_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index e63b46ab70548..4c496ade81cda 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -3,11 +3,210 @@ #![stable(feature = "std_panic", since = "1.9.0")] use crate::any::Any; -use crate::collections; -use crate::panicking; use crate::sync::atomic::{AtomicU8, Ordering}; use crate::sync::{Condvar, Mutex, RwLock}; use crate::thread::Result; +use crate::{collections, fmt, panicking}; + +#[stable(feature = "panic_hooks", since = "1.10.0")] +#[deprecated( + since = "1.82.0", + note = "use `PanicHookInfo` instead", + suggestion = "std::panic::PanicHookInfo" +)] +/// A struct providing information about a panic. +/// +/// `PanicInfo` has been renamed to [`PanicHookInfo`] to avoid confusion with +/// [`core::panic::PanicInfo`]. +pub type PanicInfo<'a> = PanicHookInfo<'a>; + +/// A struct providing information about a panic. +/// +/// `PanicHookInfo` structure is passed to a panic hook set by the [`set_hook`] function. +/// +/// # Examples +/// +/// ```should_panic +/// use std::panic; +/// +/// panic::set_hook(Box::new(|panic_info| { +/// println!("panic occurred: {panic_info}"); +/// })); +/// +/// panic!("critical system failure"); +/// ``` +/// +/// [`set_hook`]: ../../std/panic/fn.set_hook.html +#[stable(feature = "panic_hook_info", since = "1.81.0")] +#[derive(Debug)] +pub struct PanicHookInfo<'a> { + payload: &'a (dyn Any + Send), + location: &'a Location<'a>, + can_unwind: bool, + force_no_backtrace: bool, +} + +impl<'a> PanicHookInfo<'a> { + #[inline] + pub(crate) fn new( + location: &'a Location<'a>, + payload: &'a (dyn Any + Send), + can_unwind: bool, + force_no_backtrace: bool, + ) -> Self { + PanicHookInfo { payload, location, can_unwind, force_no_backtrace } + } + + /// Returns the payload associated with the panic. + /// + /// This will commonly, but not always, be a `&'static str` or [`String`]. + /// + /// A invocation of the `panic!()` macro in Rust 2021 or later will always result in a + /// panic payload of type `&'static str` or `String`. + /// + /// Only an invocation of [`panic_any`] + /// (or, in Rust 2018 and earlier, `panic!(x)` where `x` is something other than a string) + /// can result in a panic payload other than a `&'static str` or `String`. + /// + /// [`String`]: ../../std/string/struct.String.html + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(s) = panic_info.payload().downcast_ref::<&str>() { + /// println!("panic occurred: {s:?}"); + /// } else if let Some(s) = panic_info.payload().downcast_ref::() { + /// println!("panic occurred: {s:?}"); + /// } else { + /// println!("panic occurred"); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[must_use] + #[inline] + #[stable(feature = "panic_hooks", since = "1.10.0")] + pub fn payload(&self) -> &(dyn Any + Send) { + self.payload + } + + /// Returns the payload associated with the panic, if it is a string. + /// + /// This returns the payload if it is of type `&'static str` or `String`. + /// + /// A invocation of the `panic!()` macro in Rust 2021 or later will always result in a + /// panic payload where `payload_as_str` returns `Some`. + /// + /// Only an invocation of [`panic_any`] + /// (or, in Rust 2018 and earlier, `panic!(x)` where `x` is something other than a string) + /// can result in a panic payload where `payload_as_str` returns `None`. + /// + /// # Example + /// + /// ```should_panic + /// #![feature(panic_payload_as_str)] + /// + /// std::panic::set_hook(Box::new(|panic_info| { + /// if let Some(s) = panic_info.payload_as_str() { + /// println!("panic occurred: {s:?}"); + /// } else { + /// println!("panic occurred"); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "panic_payload_as_str", issue = "125175")] + pub fn payload_as_str(&self) -> Option<&str> { + if let Some(s) = self.payload.downcast_ref::<&str>() { + Some(s) + } else if let Some(s) = self.payload.downcast_ref::() { + Some(s) + } else { + None + } + } + + /// Returns information about the location from which the panic originated, + /// if available. + /// + /// This method will currently always return [`Some`], but this may change + /// in future versions. + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(location) = panic_info.location() { + /// println!("panic occurred in file '{}' at line {}", + /// location.file(), + /// location.line(), + /// ); + /// } else { + /// println!("panic occurred but can't get location information..."); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` + #[must_use] + #[inline] + #[stable(feature = "panic_hooks", since = "1.10.0")] + pub fn location(&self) -> Option<&Location<'_>> { + // NOTE: If this is changed to sometimes return None, + // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. + Some(&self.location) + } + + /// Returns whether the panic handler is allowed to unwind the stack from + /// the point where the panic occurred. + /// + /// This is true for most kinds of panics with the exception of panics + /// caused by trying to unwind out of a `Drop` implementation or a function + /// whose ABI does not support unwinding. + /// + /// It is safe for a panic handler to unwind even when this function returns + /// false, however this will simply cause the panic handler to be called + /// again. + #[must_use] + #[inline] + #[unstable(feature = "panic_can_unwind", issue = "92988")] + pub fn can_unwind(&self) -> bool { + self.can_unwind + } + + #[unstable( + feature = "panic_internals", + reason = "internal details of the implementation of the `panic!` and related macros", + issue = "none" + )] + #[doc(hidden)] + #[inline] + pub fn force_no_backtrace(&self) -> bool { + self.force_no_backtrace + } +} + +#[stable(feature = "panic_hook_display", since = "1.26.0")] +impl fmt::Display for PanicHookInfo<'_> { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("panicked at ")?; + self.location.fmt(formatter)?; + if let Some(payload) = self.payload_as_str() { + formatter.write_str(":\n")?; + formatter.write_str(payload)?; + } + Ok(()) + } +} #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] @@ -35,25 +234,22 @@ pub macro panic_2015 { #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] pub use core::panic::panic_2021; - #[stable(feature = "panic_hooks", since = "1.10.0")] -pub use crate::panicking::{set_hook, take_hook}; +pub use core::panic::Location; +#[stable(feature = "catch_unwind", since = "1.9.0")] +pub use core::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; #[unstable(feature = "panic_update_hook", issue = "92649")] pub use crate::panicking::update_hook; - #[stable(feature = "panic_hooks", since = "1.10.0")] -pub use core::panic::{Location, PanicInfo}; - -#[stable(feature = "catch_unwind", since = "1.9.0")] -pub use core::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe}; +pub use crate::panicking::{set_hook, take_hook}; -/// Panic the current thread with the given message as the panic payload. +/// Panics the current thread with the given message as the panic payload. /// /// The message can be of any (`Any + Send`) type, not just strings. /// /// The message is wrapped in a `Box<'static + Any + Send>`, which can be -/// accessed later using [`PanicInfo::payload`]. +/// accessed later using [`PanicHookInfo::payload`]. /// /// See the [`panic!`] macro for more information about panicking. #[stable(feature = "panic_any", since = "1.51.0")] @@ -179,7 +375,7 @@ pub fn resume_unwind(payload: Box) -> ! { panicking::rust_panic_without_hook(payload) } -/// Make all future panics abort directly without running the panic hook or unwinding. +/// Makes all future panics abort directly without running the panic hook or unwinding. /// /// There is no way to undo this; the effect lasts until the process exits or /// execs (or the equivalent). @@ -260,18 +456,17 @@ impl BacktraceStyle { // Internally stores equivalent of an Option. static SHOULD_CAPTURE: AtomicU8 = AtomicU8::new(0); -/// Configure whether the default panic hook will capture and display a +/// Configures whether the default panic hook will capture and display a /// backtrace. /// /// The default value for this setting may be set by the `RUST_BACKTRACE` /// environment variable; see the details in [`get_backtrace_style`]. #[unstable(feature = "panic_backtrace_config", issue = "93346")] pub fn set_backtrace_style(style: BacktraceStyle) { - if !cfg!(feature = "backtrace") { - // If the `backtrace` feature of this crate isn't enabled, skip setting. - return; + if cfg!(feature = "backtrace") { + // If the `backtrace` feature of this crate is enabled, set the backtrace style. + SHOULD_CAPTURE.store(style.as_u8(), Ordering::Release); } - SHOULD_CAPTURE.store(style.as_u8(), Ordering::Release); } /// Checks whether the standard library's panic hook will capture and print a @@ -307,21 +502,13 @@ pub fn get_backtrace_style() -> Option { return Some(style); } - let format = crate::env::var_os("RUST_BACKTRACE") - .map(|x| { - if &x == "0" { - BacktraceStyle::Off - } else if &x == "full" { - BacktraceStyle::Full - } else { - BacktraceStyle::Short - } - }) - .unwrap_or(if crate::sys::FULL_BACKTRACE_DEFAULT { - BacktraceStyle::Full - } else { - BacktraceStyle::Off - }); + let format = match crate::env::var_os("RUST_BACKTRACE") { + Some(x) if &x == "0" => BacktraceStyle::Off, + Some(x) if &x == "full" => BacktraceStyle::Full, + Some(_) => BacktraceStyle::Short, + None if crate::sys::FULL_BACKTRACE_DEFAULT => BacktraceStyle::Full, + None => BacktraceStyle::Off, + }; set_backtrace_style(format); Some(format) } diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 5699937cdb49b..e818b448270dd 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -9,26 +9,23 @@ #![deny(unsafe_op_in_unsafe_fn)] -use crate::panic::BacktraceStyle; -use core::panic::{Location, PanicInfo, PanicPayload}; +use core::panic::{Location, PanicPayload}; + +// make sure to use the stderr output configured +// by libtest in the real copy of std +#[cfg(test)] +use realstd::io::try_set_output_capture; use crate::any::Any; -use crate::fmt; -use crate::intrinsics; +#[cfg(not(test))] +use crate::io::try_set_output_capture; use crate::mem::{self, ManuallyDrop}; -use crate::process; +use crate::panic::{BacktraceStyle, PanicHookInfo}; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::{PoisonError, RwLock}; +use crate::sys::backtrace; use crate::sys::stdio::panic_output; -use crate::sys_common::backtrace; -use crate::thread; - -#[cfg(not(test))] -use crate::io::try_set_output_capture; -// make sure to use the stderr output configured -// by libtest in the real copy of std -#[cfg(test)] -use realstd::io::try_set_output_capture; +use crate::{fmt, intrinsics, process, thread}; // Binary interface to the panic runtime that the standard library depends on. // @@ -70,12 +67,12 @@ extern "C" fn __rust_foreign_exception() -> ! { enum Hook { Default, - Custom(Box) + 'static + Sync + Send>), + Custom(Box) + 'static + Sync + Send>), } impl Hook { #[inline] - fn into_box(self) -> Box) + 'static + Sync + Send> { + fn into_box(self) -> Box) + 'static + Sync + Send> { match self { Hook::Default => Box::new(default_hook), Hook::Custom(hook) => hook, @@ -105,7 +102,7 @@ static HOOK: RwLock = RwLock::new(Hook::Default); /// /// [`take_hook`]: ./fn.take_hook.html /// -/// The hook is provided with a `PanicInfo` struct which contains information +/// The hook is provided with a `PanicHookInfo` struct which contains information /// about the origin of the panic, including the payload passed to `panic!` and /// the source code location from which the panic originated. /// @@ -129,7 +126,7 @@ static HOOK: RwLock = RwLock::new(Hook::Default); /// panic!("Normal panic"); /// ``` #[stable(feature = "panic_hooks", since = "1.10.0")] -pub fn set_hook(hook: Box) + 'static + Sync + Send>) { +pub fn set_hook(hook: Box) + 'static + Sync + Send>) { if thread::panicking() { panic!("cannot modify the panic hook from a panicking thread"); } @@ -173,7 +170,7 @@ pub fn set_hook(hook: Box) + 'static + Sync + Send>) { /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] -pub fn take_hook() -> Box) + 'static + Sync + Send> { +pub fn take_hook() -> Box) + 'static + Sync + Send> { if thread::panicking() { panic!("cannot modify the panic hook from a panicking thread"); } @@ -219,7 +216,7 @@ pub fn take_hook() -> Box) + 'static + Sync + Send> { #[unstable(feature = "panic_update_hook", issue = "92649")] pub fn update_hook(hook_fn: F) where - F: Fn(&(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static), &PanicInfo<'_>) + F: Fn(&(dyn Fn(&PanicHookInfo<'_>) + Send + Sync + 'static), &PanicHookInfo<'_>) + Sync + Send + 'static, @@ -234,7 +231,7 @@ where } /// The default panic handler. -fn default_hook(info: &PanicInfo<'_>) { +fn default_hook(info: &PanicHookInfo<'_>) { // If this is a double panic, make sure that we print a backtrace // for this panic. Otherwise only print it if logging is enabled. let backtrace = if info.force_no_backtrace() { @@ -248,27 +245,25 @@ fn default_hook(info: &PanicInfo<'_>) { // The current implementation always returns `Some`. let location = info.location().unwrap(); - let msg = match info.payload().downcast_ref::<&'static str>() { - Some(s) => *s, - None => match info.payload().downcast_ref::() { - Some(s) => &s[..], - None => "Box", - }, - }; + let msg = payload_as_str(info.payload()); let thread = thread::try_current(); let name = thread.as_ref().and_then(|t| t.name()).unwrap_or(""); let write = |err: &mut dyn crate::io::Write| { + // Use a lock to prevent mixed output in multithreading context. + // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows. + let mut lock = backtrace::lock(); let _ = writeln!(err, "thread '{name}' panicked at {location}:\n{msg}"); static FIRST_PANIC: AtomicBool = AtomicBool::new(true); match backtrace { + // SAFETY: we took out a lock just a second ago. Some(BacktraceStyle::Short) => { - drop(backtrace::print(err, crate::backtrace_rs::PrintFmt::Short)) + drop(lock.print(err, crate::backtrace_rs::PrintFmt::Short)) } Some(BacktraceStyle::Full) => { - drop(backtrace::print(err, crate::backtrace_rs::PrintFmt::Full)) + drop(lock.print(err, crate::backtrace_rs::PrintFmt::Full)) } Some(BacktraceStyle::Off) => { if FIRST_PANIC.swap(false, Ordering::Relaxed) { @@ -597,31 +592,26 @@ pub fn panicking() -> bool { /// Entry point of panics from the core crate (`panic_impl` lang item). #[cfg(not(any(test, doctest)))] #[panic_handler] -pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { +pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { struct FormatStringPayload<'a> { - inner: &'a fmt::Arguments<'a>, + inner: &'a core::panic::PanicMessage<'a>, string: Option, } - impl<'a> FormatStringPayload<'a> { - fn new(inner: &'a fmt::Arguments<'a>) -> Self { - Self { inner, string: None } - } - + impl FormatStringPayload<'_> { fn fill(&mut self) -> &mut String { - use crate::fmt::Write; - let inner = self.inner; // Lazily, the first time this gets called, run the actual string formatting. self.string.get_or_insert_with(|| { let mut s = String::new(); - let _err = s.write_fmt(*inner); + let mut fmt = fmt::Formatter::new(&mut s); + let _err = fmt::Display::fmt(&inner, &mut fmt); s }) } } - unsafe impl<'a> PanicPayload for FormatStringPayload<'a> { + unsafe impl PanicPayload for FormatStringPayload<'_> { fn take_box(&mut self) -> *mut (dyn Any + Send) { // We do two allocations here, unfortunately. But (a) they're required with the current // scheme, and (b) we don't handle panic + OOM properly anyway (see comment in @@ -635,6 +625,16 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { } } + impl fmt::Display for FormatStringPayload<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(s) = &self.string { + f.write_str(s) + } else { + fmt::Display::fmt(&self.inner, f) + } + } + } + struct StaticStrPayload(&'static str); unsafe impl PanicPayload for StaticStrPayload { @@ -645,25 +645,31 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { fn get(&mut self) -> &(dyn Any + Send) { &self.0 } + + fn as_str(&mut self) -> Option<&str> { + Some(self.0) + } + } + + impl fmt::Display for StaticStrPayload { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.0) + } } let loc = info.location().unwrap(); // The current implementation always returns Some - let msg = info.message().unwrap(); // The current implementation always returns Some - crate::sys_common::backtrace::__rust_end_short_backtrace(move || { - // FIXME: can we just pass `info` along rather than taking it apart here, only to have - // `rust_panic_with_hook` construct a new `PanicInfo`? - if let Some(msg) = msg.as_str() { + let msg = info.message(); + crate::sys::backtrace::__rust_end_short_backtrace(move || { + if let Some(s) = msg.as_str() { rust_panic_with_hook( - &mut StaticStrPayload(msg), - info.message(), + &mut StaticStrPayload(s), loc, info.can_unwind(), info.force_no_backtrace(), ); } else { rust_panic_with_hook( - &mut FormatStringPayload::new(msg), - info.message(), + &mut FormatStringPayload { inner: &msg, string: None }, loc, info.can_unwind(), info.force_no_backtrace(), @@ -689,27 +695,10 @@ pub const fn begin_panic(msg: M) -> ! { intrinsics::abort() } - let loc = Location::caller(); - return crate::sys_common::backtrace::__rust_end_short_backtrace(move || { - rust_panic_with_hook( - &mut Payload::new(msg), - None, - loc, - /* can_unwind */ true, - /* force_no_backtrace */ false, - ) - }); - struct Payload { inner: Option, } - impl Payload { - fn new(inner: A) -> Payload { - Payload { inner: Some(inner) } - } - } - unsafe impl PanicPayload for Payload { fn take_box(&mut self) -> *mut (dyn Any + Send) { // Note that this should be the only allocation performed in this code path. Currently @@ -731,6 +720,35 @@ pub const fn begin_panic(msg: M) -> ! { } } } + + impl fmt::Display for Payload { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.inner { + Some(a) => f.write_str(payload_as_str(a)), + None => process::abort(), + } + } + } + + let loc = Location::caller(); + crate::sys::backtrace::__rust_end_short_backtrace(move || { + rust_panic_with_hook( + &mut Payload { inner: Some(msg) }, + loc, + /* can_unwind */ true, + /* force_no_backtrace */ false, + ) + }) +} + +fn payload_as_str(payload: &dyn Any) -> &str { + if let Some(&s) = payload.downcast_ref::<&'static str>() { + s + } else if let Some(s) = payload.downcast_ref::() { + s.as_str() + } else { + "Box" + } } /// Central point for dispatching panics. @@ -740,7 +758,6 @@ pub const fn begin_panic(msg: M) -> ! { /// abort or unwind. fn rust_panic_with_hook( payload: &mut dyn PanicPayload, - message: Option<&fmt::Arguments<'_>>, location: &Location<'_>, can_unwind: bool, force_no_backtrace: bool, @@ -754,35 +771,21 @@ fn rust_panic_with_hook( // Don't try to format the message in this case, perhaps that is causing the // recursive panics. However if the message is just a string, no user-defined // code is involved in printing it, so that is risk-free. - let msg_str = message.and_then(|m| m.as_str()).map(|m| [m]); - let message = msg_str.as_ref().map(|m| fmt::Arguments::new_const(m)); - let panicinfo = PanicInfo::internal_constructor( - message.as_ref(), - location, - can_unwind, - force_no_backtrace, + let message: &str = payload.as_str().unwrap_or_default(); + rtprintpanic!( + "panicked at {location}:\n{message}\nthread panicked while processing panic. aborting.\n" ); - rtprintpanic!("{panicinfo}\nthread panicked while processing panic. aborting.\n"); } panic_count::MustAbort::AlwaysAbort => { // Unfortunately, this does not print a backtrace, because creating // a `Backtrace` will allocate, which we must avoid here. - let panicinfo = PanicInfo::internal_constructor( - message, - location, - can_unwind, - force_no_backtrace, - ); - rtprintpanic!("{panicinfo}\npanicked after panic::always_abort(), aborting.\n"); + rtprintpanic!("aborting due to panic at {location}:\n{payload}\n"); } } crate::sys::abort_internal(); } - let mut info = - PanicInfo::internal_constructor(message, location, can_unwind, force_no_backtrace); - let hook = HOOK.read().unwrap_or_else(PoisonError::into_inner); - match *hook { + match *HOOK.read().unwrap_or_else(PoisonError::into_inner) { // Some platforms (like wasm) know that printing to stderr won't ever actually // print anything, and if that's the case we can skip the default // hook. Since string formatting happens lazily when calling `payload` @@ -791,15 +794,17 @@ fn rust_panic_with_hook( // formatting.) Hook::Default if panic_output().is_none() => {} Hook::Default => { - info.set_payload(payload.get()); - default_hook(&info); + default_hook(&PanicHookInfo::new( + location, + payload.get(), + can_unwind, + force_no_backtrace, + )); } Hook::Custom(ref hook) => { - info.set_payload(payload.get()); - hook(&info); + hook(&PanicHookInfo::new(location, payload.get(), can_unwind, force_no_backtrace)); } - }; - drop(hook); + } // Indicate that we have finished executing the panic hook. After this point // it is fine if there is a panic while executing destructors, as long as it @@ -835,6 +840,12 @@ pub fn rust_panic_without_hook(payload: Box) -> ! { } } + impl fmt::Display for RewrapBox { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(payload_as_str(&self.0)) + } + } + rust_panic(&mut RewrapBox(payload)) } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index f835b69f0cfb5..80163667636ae 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -71,22 +71,17 @@ mod tests; use crate::borrow::{Borrow, Cow}; -use crate::cmp; use crate::collections::TryReserveError; use crate::error::Error; -use crate::fmt; -use crate::fs; +use crate::ffi::{os_str, OsStr, OsString}; use crate::hash::{Hash, Hasher}; -use crate::io; use crate::iter::FusedIterator; use crate::ops::{self, Deref}; use crate::rc::Rc; use crate::str::FromStr; use crate::sync::Arc; - -use crate::ffi::{os_str, OsStr, OsString}; -use crate::sys; use crate::sys::path::{is_sep_byte, is_verbatim_sep, parse_prefix, MAIN_SEP_STR}; +use crate::{cmp, fmt, fs, io, sys}; //////////////////////////////////////////////////////////////////////////////// // GENERAL NOTES @@ -1163,11 +1158,6 @@ pub struct PathBuf { } impl PathBuf { - #[inline] - fn as_mut_vec(&mut self) -> &mut Vec { - self.inner.as_mut_vec_for_path_buf() - } - /// Allocates an empty `PathBuf`. /// /// # Examples @@ -1226,6 +1216,25 @@ impl PathBuf { self } + /// Consumes and leaks the `PathBuf`, returning a mutable reference to the contents, + /// `&'a mut Path`. + /// + /// The caller has free choice over the returned lifetime, including 'static. + /// Indeed, this function is ideally used for data that lives for the remainder of + /// the program’s life, as dropping the returned reference will cause a memory leak. + /// + /// It does not reallocate or shrink the `PathBuf`, so the leaked allocation may include + /// unused capacity that is not part of the returned slice. If you want to discard excess + /// capacity, call [`into_boxed_path`], and then [`Box::leak`] instead. + /// However, keep in mind that trimming the capacity may result in a reallocation and copy. + /// + /// [`into_boxed_path`]: Self::into_boxed_path + #[unstable(feature = "os_string_pathbuf_leak", issue = "125965")] + #[inline] + pub fn leak<'a>(self) -> &'a mut Path { + Path::from_inner_mut(self.inner.leak()) + } + /// Extends `self` with `path`. /// /// If `path` is absolute, it replaces the current path. @@ -1271,7 +1280,8 @@ impl PathBuf { fn _push(&mut self, path: &Path) { // in general, a separator is needed if the rightmost byte is not a separator - let mut need_sep = self.as_mut_vec().last().map(|c| !is_sep_byte(*c)).unwrap_or(false); + let buf = self.inner.as_encoded_bytes(); + let mut need_sep = buf.last().map(|c| !is_sep_byte(*c)).unwrap_or(false); // in the special case of `C:` on Windows, do *not* add a separator let comps = self.components(); @@ -1285,7 +1295,7 @@ impl PathBuf { // absolute `path` replaces `self` if path.is_absolute() || path.prefix().is_some() { - self.as_mut_vec().truncate(0); + self.inner.truncate(0); // verbatim paths need . and .. removed } else if comps.prefix_verbatim() && !path.inner.is_empty() { @@ -1330,7 +1340,7 @@ impl PathBuf { // `path` has a root but no prefix, e.g., `\windows` (Windows only) } else if path.has_root() { let prefix_len = self.components().prefix_remaining(); - self.as_mut_vec().truncate(prefix_len); + self.inner.truncate(prefix_len); // `path` is a pure relative path } else if need_sep { @@ -1363,7 +1373,7 @@ impl PathBuf { pub fn pop(&mut self) -> bool { match self.parent().map(|p| p.as_u8_slice().len()) { Some(len) => { - self.as_mut_vec().truncate(len); + self.inner.truncate(len); true } None => false, @@ -1425,6 +1435,11 @@ impl PathBuf { /// If `extension` is the empty string, [`self.extension`] will be [`None`] /// afterwards, not `Some("")`. /// + /// # Panics + /// + /// Panics if the passed extension contains a path separator (see + /// [`is_separator`]). + /// /// # Caveats /// /// The new `extension` may contain dots and will be used in its entirety, @@ -1470,6 +1485,14 @@ impl PathBuf { } fn _set_extension(&mut self, extension: &OsStr) -> bool { + for &b in extension.as_encoded_bytes() { + if b < 128 { + if is_separator(b as char) { + panic!("extension cannot contain path separators: {:?}", extension); + } + } + } + let file_stem = match self.file_stem() { None => return false, Some(f) => f.as_encoded_bytes(), @@ -1478,15 +1501,82 @@ impl PathBuf { // truncate until right after the file stem let end_file_stem = file_stem[file_stem.len()..].as_ptr().addr(); let start = self.inner.as_encoded_bytes().as_ptr().addr(); - let v = self.as_mut_vec(); - v.truncate(end_file_stem.wrapping_sub(start)); + self.inner.truncate(end_file_stem.wrapping_sub(start)); // add the new extension, if any - let new = extension.as_encoded_bytes(); + let new = extension; if !new.is_empty() { - v.reserve_exact(new.len() + 1); - v.push(b'.'); - v.extend_from_slice(new); + self.inner.reserve_exact(new.len() + 1); + self.inner.push(OsStr::new(".")); + self.inner.push(new); + } + + true + } + + /// Append [`self.extension`] with `extension`. + /// + /// Returns `false` and does nothing if [`self.file_name`] is [`None`], + /// returns `true` and updates the extension otherwise. + /// + /// # Caveats + /// + /// The appended `extension` may contain dots and will be used in its entirety, + /// but only the part after the final dot will be reflected in + /// [`self.extension`]. + /// + /// See the examples below. + /// + /// [`self.file_name`]: Path::file_name + /// [`self.extension`]: Path::extension + /// + /// # Examples + /// + /// ``` + /// #![feature(path_add_extension)] + /// + /// use std::path::{Path, PathBuf}; + /// + /// let mut p = PathBuf::from("/feel/the"); + /// + /// p.add_extension("formatted"); + /// assert_eq!(Path::new("/feel/the.formatted"), p.as_path()); + /// + /// p.add_extension("dark.side"); + /// assert_eq!(Path::new("/feel/the.formatted.dark.side"), p.as_path()); + /// + /// p.set_extension("cookie"); + /// assert_eq!(Path::new("/feel/the.formatted.dark.cookie"), p.as_path()); + /// + /// p.set_extension(""); + /// assert_eq!(Path::new("/feel/the.formatted.dark"), p.as_path()); + /// + /// p.add_extension(""); + /// assert_eq!(Path::new("/feel/the.formatted.dark"), p.as_path()); + /// ``` + #[unstable(feature = "path_add_extension", issue = "127292")] + pub fn add_extension>(&mut self, extension: S) -> bool { + self._add_extension(extension.as_ref()) + } + + fn _add_extension(&mut self, extension: &OsStr) -> bool { + let file_name = match self.file_name() { + None => return false, + Some(f) => f.as_encoded_bytes(), + }; + + let new = extension; + if !new.is_empty() { + // truncate until right after the file name + // this is necessary for trimming the trailing slash + let end_file_name = file_name[file_name.len()..].as_ptr().addr(); + let start = self.inner.as_encoded_bytes().as_ptr().addr(); + self.inner.truncate(end_file_name.wrapping_sub(start)); + + // append the new extension + self.inner.reserve_exact(new.len() + 1); + self.inner.push(OsStr::new(".")); + self.inner.push(new); } true @@ -1702,7 +1792,7 @@ impl> From<&T> for PathBuf { #[stable(feature = "rust1", since = "1.0.0")] impl From for PathBuf { - /// Converts an [`OsString`] into a [`PathBuf`] + /// Converts an [`OsString`] into a [`PathBuf`]. /// /// This conversion does not allocate or copy memory. #[inline] @@ -1984,10 +2074,8 @@ impl AsRef for PathBuf { #[stable(feature = "rust1", since = "1.0.0")] // `Path::new` current implementation relies // on `Path` being layout-compatible with `OsStr`. -// However, `Path` layout is considered an implementation detail and must not be relied upon. We -// want `repr(transparent)` but we don't want it to show up in rustdoc, so we hide it under -// `cfg(doc)`. This is an ad-hoc implementation of attribute privacy. -#[cfg_attr(not(doc), repr(transparent))] +// However, `Path` layout is considered an implementation detail and must not be relied upon. +#[repr(transparent)] pub struct Path { inner: OsStr, } @@ -2613,22 +2701,48 @@ impl Path { None => { // Enough capacity for the extension and the dot let capacity = self_len + extension.len() + 1; - let whole_path = self_bytes.iter(); + let whole_path = self_bytes; (capacity, whole_path) } Some(previous_extension) => { let capacity = self_len + extension.len() - previous_extension.len(); - let path_till_dot = self_bytes[..self_len - previous_extension.len()].iter(); + let path_till_dot = &self_bytes[..self_len - previous_extension.len()]; (capacity, path_till_dot) } }; let mut new_path = PathBuf::with_capacity(new_capacity); - new_path.as_mut_vec().extend(slice_to_copy); + new_path.inner.extend_from_slice(slice_to_copy); new_path.set_extension(extension); new_path } + /// Creates an owned [`PathBuf`] like `self` but with the extension added. + /// + /// See [`PathBuf::add_extension`] for more details. + /// + /// # Examples + /// + /// ``` + /// #![feature(path_add_extension)] + /// + /// use std::path::{Path, PathBuf}; + /// + /// let path = Path::new("foo.rs"); + /// assert_eq!(path.with_added_extension("txt"), PathBuf::from("foo.rs.txt")); + /// + /// let path = Path::new("foo.tar.gz"); + /// assert_eq!(path.with_added_extension(""), PathBuf::from("foo.tar.gz")); + /// assert_eq!(path.with_added_extension("xz"), PathBuf::from("foo.tar.gz.xz")); + /// assert_eq!(path.with_added_extension("").with_added_extension("txt"), PathBuf::from("foo.tar.gz.txt")); + /// ``` + #[unstable(feature = "path_add_extension", issue = "127292")] + pub fn with_added_extension>(&self, extension: S) -> PathBuf { + let mut new_path = self.to_path_buf(); + new_path.add_extension(extension); + new_path + } + /// Produces an iterator over the [`Component`]s of the path. /// /// When parsing the path, there is a small amount of normalization: @@ -2875,6 +2989,8 @@ impl Path { /// prevent time-of-check to time-of-use (TOCTOU) bugs. You should only use it in scenarios /// where those bugs are not an issue. /// + /// This is an alias for [`std::fs::exists`](crate::fs::exists). + /// /// # Examples /// /// ```no_run @@ -2887,7 +3003,7 @@ impl Path { #[stable(feature = "path_try_exists", since = "1.63.0")] #[inline] pub fn try_exists(&self) -> io::Result { - fs::try_exists(self) + fs::exists(self) } /// Returns `true` if the path exists on disk and is pointing at a regular file. @@ -3069,15 +3185,19 @@ impl Hash for Path { let bytes = &bytes[prefix_len..]; let mut component_start = 0; - let mut bytes_hashed = 0; + // track some extra state to avoid prefix collisions. + // ["foo", "bar"] and ["foobar"], will have the same payload bytes + // but result in different chunk_bits + let mut chunk_bits: usize = 0; for i in 0..bytes.len() { let is_sep = if verbatim { is_verbatim_sep(bytes[i]) } else { is_sep_byte(bytes[i]) }; if is_sep { if i > component_start { let to_hash = &bytes[component_start..i]; + chunk_bits = chunk_bits.wrapping_add(to_hash.len()); + chunk_bits = chunk_bits.rotate_right(2); h.write(to_hash); - bytes_hashed += to_hash.len(); } // skip over separator and optionally a following CurDir item @@ -3098,11 +3218,12 @@ impl Hash for Path { if component_start < bytes.len() { let to_hash = &bytes[component_start..]; + chunk_bits = chunk_bits.wrapping_add(to_hash.len()); + chunk_bits = chunk_bits.rotate_right(2); h.write(to_hash); - bytes_hashed += to_hash.len(); } - h.write_usize(bytes_hashed); + h.write_usize(chunk_bits); } } @@ -3313,14 +3434,33 @@ impl Error for StripPrefixError { /// Makes the path absolute without accessing the filesystem. /// /// If the path is relative, the current directory is used as the base directory. -/// All intermediate components will be resolved according to platforms-specific -/// rules but unlike [`canonicalize`][crate::fs::canonicalize] this does not +/// All intermediate components will be resolved according to platform-specific +/// rules, but unlike [`canonicalize`][crate::fs::canonicalize], this does not /// resolve symlinks and may succeed even if the path does not exist. /// /// If the `path` is empty or getting the -/// [current directory][crate::env::current_dir] fails then an error will be +/// [current directory][crate::env::current_dir] fails, then an error will be /// returned. /// +/// # Platform-specific behavior +/// +/// On POSIX platforms, the path is resolved using [POSIX semantics][posix-semantics], +/// except that it stops short of resolving symlinks. This means it will keep `..` +/// components and trailing slashes. +/// +/// On Windows, for verbatim paths, this will simply return the path as given. For other +/// paths, this is currently equivalent to calling +/// [`GetFullPathNameW`][windows-path]. +/// +/// Note that these [may change in the future][changes]. +/// +/// # Errors +/// +/// This function may return an error in the following situations: +/// +/// * If `path` is syntactically invalid; in particular, if it is empty. +/// * If getting the [current directory][crate::env::current_dir] fails. +/// /// # Examples /// /// ## POSIX paths @@ -3328,50 +3468,42 @@ impl Error for StripPrefixError { /// ``` /// # #[cfg(unix)] /// fn main() -> std::io::Result<()> { -/// use std::path::{self, Path}; +/// use std::path::{self, Path}; /// -/// // Relative to absolute -/// let absolute = path::absolute("foo/./bar")?; -/// assert!(absolute.ends_with("foo/bar")); +/// // Relative to absolute +/// let absolute = path::absolute("foo/./bar")?; +/// assert!(absolute.ends_with("foo/bar")); /// -/// // Absolute to absolute -/// let absolute = path::absolute("/foo//test/.././bar.rs")?; -/// assert_eq!(absolute, Path::new("/foo/test/../bar.rs")); -/// Ok(()) +/// // Absolute to absolute +/// let absolute = path::absolute("/foo//test/.././bar.rs")?; +/// assert_eq!(absolute, Path::new("/foo/test/../bar.rs")); +/// Ok(()) /// } /// # #[cfg(not(unix))] /// # fn main() {} /// ``` /// -/// The path is resolved using [POSIX semantics][posix-semantics] except that -/// it stops short of resolving symlinks. This means it will keep `..` -/// components and trailing slashes. -/// /// ## Windows paths /// /// ``` /// # #[cfg(windows)] /// fn main() -> std::io::Result<()> { -/// use std::path::{self, Path}; +/// use std::path::{self, Path}; /// -/// // Relative to absolute -/// let absolute = path::absolute("foo/./bar")?; -/// assert!(absolute.ends_with(r"foo\bar")); +/// // Relative to absolute +/// let absolute = path::absolute("foo/./bar")?; +/// assert!(absolute.ends_with(r"foo\bar")); /// -/// // Absolute to absolute -/// let absolute = path::absolute(r"C:\foo//test\..\./bar.rs")?; +/// // Absolute to absolute +/// let absolute = path::absolute(r"C:\foo//test\..\./bar.rs")?; /// -/// assert_eq!(absolute, Path::new(r"C:\foo\bar.rs")); -/// Ok(()) +/// assert_eq!(absolute, Path::new(r"C:\foo\bar.rs")); +/// Ok(()) /// } /// # #[cfg(not(windows))] /// # fn main() {} /// ``` /// -/// For verbatim paths this will simply return the path as given. For other -/// paths this is currently equivalent to calling -/// [`GetFullPathNameW`][windows-path]. -/// /// Note that this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index fde6ed4f0c057..a12e42cba0c5c 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1,8 +1,8 @@ -use super::*; +use core::hint::black_box; +use super::*; use crate::collections::{BTreeSet, HashSet}; use crate::hash::DefaultHasher; -use core::hint::black_box; #[allow(unknown_lints, unused_macro_rules)] macro_rules! t ( @@ -126,6 +126,16 @@ fn into() { assert_eq!(static_cow_path, owned_cow_path); } +#[test] +fn test_pathbuf_leak() { + let string = "/have/a/cake".to_owned(); + let (len, cap) = (string.len(), string.capacity()); + let buf = PathBuf::from(string); + let leaked = buf.leak(); + assert_eq!(leaked.as_os_str().as_encoded_bytes(), b"/have/a/cake"); + unsafe { drop(String::from_raw_parts(leaked.as_mut_os_str() as *mut OsStr as _, len, cap)) } +} + #[test] #[cfg(unix)] pub fn test_decompositions_unix() { @@ -1391,6 +1401,37 @@ pub fn test_set_extension() { tfe!("/", "foo", "/", false); } +#[test] +pub fn test_add_extension() { + macro_rules! tfe ( + ($path:expr, $ext:expr, $expected:expr, $output:expr) => ({ + let mut p = PathBuf::from($path); + let output = p.add_extension($ext); + assert!(p.to_str() == Some($expected) && output == $output, + "adding extension of {:?} to {:?}: Expected {:?}/{:?}, got {:?}/{:?}", + $path, $ext, $expected, $output, + p.to_str().unwrap(), output); + }); + ); + + tfe!("foo", "txt", "foo.txt", true); + tfe!("foo.bar", "txt", "foo.bar.txt", true); + tfe!("foo.bar.baz", "txt", "foo.bar.baz.txt", true); + tfe!(".test", "txt", ".test.txt", true); + tfe!("foo.txt", "", "foo.txt", true); + tfe!("foo", "", "foo", true); + tfe!("", "foo", "", false); + tfe!(".", "foo", ".", false); + tfe!("foo/", "bar", "foo.bar", true); + tfe!("foo/.", "bar", "foo.bar", true); + tfe!("..", "foo", "..", false); + tfe!("foo/..", "bar", "foo/..", false); + tfe!("/", "foo", "/", false); + + // edge cases + tfe!("/foo.ext////", "bar", "/foo.ext.bar", true); +} + #[test] pub fn test_with_extension() { macro_rules! twe ( @@ -1431,6 +1472,49 @@ pub fn test_with_extension() { twe!("ccc.bbb_bbb", "aaa_aaa_aaa", "ccc.aaa_aaa_aaa"); } +#[test] +pub fn test_with_added_extension() { + macro_rules! twe ( + ($input:expr, $extension:expr, $expected:expr) => ({ + let input = Path::new($input); + let output = input.with_added_extension($extension); + + assert!( + output.to_str() == Some($expected), + "calling Path::new({:?}).with_added_extension({:?}): Expected {:?}, got {:?}", + $input, $extension, $expected, output, + ); + }); + ); + + twe!("foo", "txt", "foo.txt"); + twe!("foo.bar", "txt", "foo.bar.txt"); + twe!("foo.bar.baz", "txt", "foo.bar.baz.txt"); + twe!(".test", "txt", ".test.txt"); + twe!("foo.txt", "", "foo.txt"); + twe!("foo", "", "foo"); + twe!("", "foo", ""); + twe!(".", "foo", "."); + twe!("foo/", "bar", "foo.bar"); + twe!("foo/.", "bar", "foo.bar"); + twe!("..", "foo", ".."); + twe!("foo/..", "bar", "foo/.."); + twe!("/", "foo", "/"); + + // edge cases + twe!("/foo.ext////", "bar", "/foo.ext.bar"); + + // New extension is smaller than file name + twe!("aaa_aaa_aaa", "bbb_bbb", "aaa_aaa_aaa.bbb_bbb"); + // New extension is greater than file name + twe!("bbb_bbb", "aaa_aaa_aaa", "bbb_bbb.aaa_aaa_aaa"); + + // New extension is smaller than previous extension + twe!("ccc.aaa_aaa_aaa", "bbb_bbb", "ccc.aaa_aaa_aaa.bbb_bbb"); + // New extension is greater than previous extension + twe!("ccc.bbb_bbb", "aaa_aaa_aaa", "ccc.bbb_bbb.aaa_aaa_aaa"); +} + #[test] fn test_eq_receivers() { use crate::borrow::Cow; @@ -1535,6 +1619,20 @@ pub fn test_compare() { relative_from: Some("") ); + tc!("foo//", "foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo///", "foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + tc!("foo/.", "foo", eq: true, starts_with: true, @@ -1549,6 +1647,20 @@ pub fn test_compare() { relative_from: Some("") ); + tc!("foo/.//bar", "foo/bar", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo//./bar", "foo/bar", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + tc!("foo/bar", "foo", eq: false, starts_with: true, @@ -1556,6 +1668,13 @@ pub fn test_compare() { relative_from: Some("bar") ); + tc!("foo/bar", "foobar", + eq: false, + starts_with: false, + ends_with: false, + relative_from: None + ); + tc!("foo/bar/baz", "foo/bar", eq: false, starts_with: true, @@ -1803,6 +1922,29 @@ fn test_windows_absolute() { assert_eq!(absolute(r"COM1").unwrap().as_os_str(), Path::new(r"\\.\COM1").as_os_str()); } +#[test] +#[should_panic = "path separator"] +fn test_extension_path_sep() { + let mut path = PathBuf::from("path/to/file"); + path.set_extension("d/../../../../../etc/passwd"); +} + +#[test] +#[should_panic = "path separator"] +#[cfg(windows)] +fn test_extension_path_sep_alternate() { + let mut path = PathBuf::from("path/to/file"); + path.set_extension("d\\test"); +} + +#[test] +#[cfg(not(windows))] +fn test_extension_path_sep_alternate() { + let mut path = PathBuf::from("path/to/file"); + path.set_extension("d\\test"); + assert_eq!(path, Path::new("path/to/file.d\\test")); +} + #[bench] #[cfg_attr(miri, ignore)] // Miri isn't fast... fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) { diff --git a/library/std/src/pipe.rs b/library/std/src/pipe.rs new file mode 100644 index 0000000000000..aa4c7014fe918 --- /dev/null +++ b/library/std/src/pipe.rs @@ -0,0 +1,128 @@ +//! Module for anonymous pipe +//! +//! ``` +//! #![feature(anonymous_pipe)] +//! +//! # #[cfg(miri)] fn main() {} +//! # #[cfg(not(miri))] +//! # fn main() -> std::io::Result<()> { +//! let (reader, writer) = std::pipe::pipe()?; +//! # Ok(()) +//! # } +//! ``` + +use crate::io; +use crate::sys::anonymous_pipe::{pipe as pipe_inner, AnonPipe}; + +/// Create anonymous pipe that is close-on-exec and blocking. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[inline] +pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) +} + +/// Read end of the anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeReader(pub(crate) AnonPipe); + +/// Write end of the anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeWriter(pub(crate) AnonPipe); + +impl PipeReader { + /// Create a new [`PipeReader`] instance that shares the same underlying file description. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +impl PipeWriter { + /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for &PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for &PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} diff --git a/library/std/src/pipe/tests.rs b/library/std/src/pipe/tests.rs new file mode 100644 index 0000000000000..9c38e10678752 --- /dev/null +++ b/library/std/src/pipe/tests.rs @@ -0,0 +1,19 @@ +use crate::io::{Read, Write}; +use crate::pipe::pipe; + +#[test] +#[cfg(all(windows, unix, not(miri)))] +fn pipe_creation_clone_and_rw() { + let (rx, tx) = pipe().unwrap(); + + tx.try_clone().unwrap().write_all(b"12345").unwrap(); + drop(tx); + + let mut rx2 = rx.try_clone().unwrap(); + drop(rx); + + let mut s = String::new(); + rx2.read_to_string(&mut s).unwrap(); + drop(rx2); + assert_eq!(s, "12345"); +} diff --git a/library/std/src/prelude/common.rs b/library/std/src/prelude/common.rs index 01936734d7548..b231bd871b3b4 100644 --- a/library/std/src/prelude/common.rs +++ b/library/std/src/prelude/common.rs @@ -2,6 +2,9 @@ //! //! See the [module-level documentation](super) for more. +// No formatting: this file is nothing but re-exports, and their order is worth preserving. +#![cfg_attr(rustfmt, rustfmt::skip)] + // Re-exported core operators #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] @@ -14,6 +17,9 @@ pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; #[stable(feature = "rust1", since = "1.0.0")] #[doc(no_inline)] pub use crate::mem::drop; +#[stable(feature = "size_of_prelude", since = "1.80.0")] +#[doc(no_inline)] +pub use crate::mem::{align_of, align_of_val, size_of, size_of_val}; // Re-exported types and traits #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 0bdbab716adb4..0c610ba67e65c 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -36,6 +36,10 @@ //! operations for both destructors and overloading `()`. //! * [std::mem]::[drop], a convenience function for explicitly //! dropping a value. +//! * [std::mem]::{[size_of], [size_of_val]}, to get the size of +//! a type or value. +//! * [std::mem]::{[align_of], [align_of_val]}, to get the +//! alignment of a type or value. //! * [std::boxed]::[Box], a way to allocate values on the heap. //! * [std::borrow]::[ToOwned], the conversion trait that defines //! [`to_owned`], the generic method for creating an owned type from a @@ -91,6 +95,9 @@ //! [book-enums]: ../../book/ch06-01-defining-an-enum.html //! [book-iter]: ../../book/ch13-02-iterators.html +// No formatting: this file is nothing but re-exports, and their order is worth preserving. +#![cfg_attr(rustfmt, rustfmt::skip)] + #![stable(feature = "rust1", since = "1.0.0")] mod common; diff --git a/library/std/src/process.rs b/library/std/src/process.rs index c926c89f7a97f..9ffdebe1b6ffe 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -96,9 +96,9 @@ //! child processes must agree on how the commandline string is encoded. //! //! Most programs use the standard C run-time `argv`, which in practice results -//! in consistent argument handling. However some programs have their own way of +//! in consistent argument handling. However, some programs have their own way of //! parsing the commandline string. In these cases using [`arg`] or [`args`] may -//! result in the child process seeing a different array of arguments then the +//! result in the child process seeing a different array of arguments than the //! parent process intended. //! //! Two ways of mitigating this are: @@ -151,21 +151,18 @@ #[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx", target_os = "xous"))))] mod tests; -use crate::io::prelude::*; - use crate::convert::Infallible; use crate::ffi::OsStr; -use crate::fmt; -use crate::fs; +use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::num::NonZero; use crate::path::Path; -use crate::str; use crate::sys::pipe::{read2, AnonPipe}; use crate::sys::process as imp; #[stable(feature = "command_access", since = "1.57.0")] pub use crate::sys_common::process::CommandEnvs; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::{fmt, fs, str}; /// Representation of a running or exited child process. /// @@ -629,6 +626,25 @@ impl Command { /// .spawn() /// .expect("sh command failed to start"); /// ``` + /// + /// # Caveats + /// + /// [`Command::new`] is only intended to accept the path of the program. If you pass a program + /// path along with arguments like `Command::new("ls -l").spawn()`, it will try to search for + /// `ls -l` literally. The arguments need to be passed separately, such as via [`arg`] or + /// [`args`]. + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .arg("-l") // arg passed separately + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + /// + /// [`arg`]: Self::arg + /// [`args`]: Self::args #[stable(feature = "process", since = "1.0.0")] pub fn new>(program: S) -> Command { Command { inner: imp::Command::new(program.as_ref()) } @@ -2037,7 +2053,7 @@ impl ExitCode { // representation of an ExitCode // // More info: https://internals.rust-lang.org/t/mini-pre-rfc-redesigning-process-exitstatus/5426 - /// Convert an `ExitCode` into an i32 + /// Converts an `ExitCode` into an i32 #[unstable( feature = "process_exitcode_internals", reason = "exposed only for libstd", @@ -2060,7 +2076,7 @@ impl Default for ExitCode { #[stable(feature = "process_exitcode", since = "1.61.0")] impl From for ExitCode { - /// Construct an `ExitCode` from an arbitrary u8 value. + /// Constructs an `ExitCode` from an arbitrary u8 value. fn from(code: u8) -> Self { ExitCode(imp::ExitCode::from(code)) } diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 07d4de5c1a26e..f8e8e0dea553b 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -1,6 +1,5 @@ -use crate::io::prelude::*; - use super::{Command, Output, Stdio}; +use crate::io::prelude::*; use crate::io::{BorrowedBuf, ErrorKind}; use crate::mem::MaybeUninit; use crate::str; @@ -137,7 +136,7 @@ fn child_stdout_read_buf() { let child = cmd.spawn().unwrap(); let mut stdout = child.stdout.unwrap(); - let mut buf: [MaybeUninit; 128] = MaybeUninit::uninit_array(); + let mut buf: [MaybeUninit; 128] = [MaybeUninit::uninit(); 128]; let mut buf = BorrowedBuf::from(buf.as_mut_slice()); stdout.read_buf(buf.unfilled()).unwrap(); @@ -385,29 +384,25 @@ fn test_interior_nul_in_env_value_is_error() { #[cfg(windows)] fn test_creation_flags() { use crate::os::windows::process::CommandExt; - use crate::sys::c::{BOOL, DWORD, INFINITE}; - #[repr(C, packed)] + use crate::sys::c::{BOOL, INFINITE}; + #[repr(C)] struct DEBUG_EVENT { - pub event_code: DWORD, - pub process_id: DWORD, - pub thread_id: DWORD, + pub event_code: u32, + pub process_id: u32, + pub thread_id: u32, // This is a union in the real struct, but we don't // need this data for the purposes of this test. pub _junk: [u8; 164], } extern "system" { - fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: DWORD) -> BOOL; - fn ContinueDebugEvent( - dwProcessId: DWORD, - dwThreadId: DWORD, - dwContinueStatus: DWORD, - ) -> BOOL; + fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: u32) -> BOOL; + fn ContinueDebugEvent(dwProcessId: u32, dwThreadId: u32, dwContinueStatus: u32) -> BOOL; } - const DEBUG_PROCESS: DWORD = 1; - const EXIT_PROCESS_DEBUG_EVENT: DWORD = 5; - const DBG_EXCEPTION_NOT_HANDLED: DWORD = 0x80010001; + const DEBUG_PROCESS: u32 = 1; + const EXIT_PROCESS_DEBUG_EVENT: u32 = 5; + const DBG_EXCEPTION_NOT_HANDLED: u32 = 0x80010001; let mut child = Command::new("cmd").creation_flags(DEBUG_PROCESS).stdin(Stdio::piped()).spawn().unwrap(); diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 46f691d7b7504..307a543c9d215 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -16,10 +16,11 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(unused_macros)] -// Re-export some of our utilities which are expected by other crates. +#[rustfmt::skip] pub use crate::panicking::{begin_panic, panic_count}; pub use core::panicking::{panic_display, panic_fmt}; +#[rustfmt::skip] use crate::sync::Once; use crate::sys; use crate::thread::{self, Thread}; @@ -90,13 +91,14 @@ macro_rules! rtunwrap { // `compiler/rustc_session/src/config/sigpipe.rs`. #[cfg_attr(test, allow(dead_code))] unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { + #[cfg_attr(target_os = "teeos", allow(unused_unsafe))] unsafe { - sys::init(argc, argv, sigpipe); + sys::init(argc, argv, sigpipe) + }; - // Set up the current thread to give it the right name. - let thread = Thread::new_main(); - thread::set_current(thread); - } + // Set up the current thread to give it the right name. + let thread = Thread::new_main(); + thread::set_current(thread); } // One-time runtime cleanup. @@ -144,6 +146,9 @@ fn lang_start_internal( rtabort!("drop of the panic payload panicked"); }); panic::catch_unwind(cleanup).map_err(rt_abort)?; + // Guard against multple threads calling `libc::exit` concurrently. + // See the documentation for `unique_thread_exit` for more information. + panic::catch_unwind(|| crate::sys::exit_guard::unique_thread_exit()).map_err(rt_abort)?; ret_code } @@ -156,7 +161,7 @@ fn lang_start( sigpipe: u8, ) -> isize { let Ok(v) = lang_start_internal( - &move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report().to_i32(), + &move || crate::sys::backtrace::__rust_begin_short_backtrace(main).report().to_i32(), argc, argv, sigpipe, diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index b4bac081e7ab7..82cc13a74b7f1 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -20,7 +20,7 @@ use crate::sync::{Condvar, Mutex}; /// let c = Arc::clone(&barrier); /// // The same messages will be printed together. /// // You will NOT see any interleaving. -/// handles.push(thread::spawn(move|| { +/// handles.push(thread::spawn(move || { /// println!("before wait"); /// c.wait(); /// println!("after wait"); @@ -115,7 +115,7 @@ impl Barrier { /// let c = Arc::clone(&barrier); /// // The same messages will be printed together. /// // You will NOT see any interleaving. - /// handles.push(thread::spawn(move|| { + /// handles.push(thread::spawn(move || { /// println!("before wait"); /// c.wait(); /// println!("after wait"); diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs index b20574e4f1493..08d46f356d9f2 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/condvar.rs @@ -35,6 +35,7 @@ impl WaitTimeoutResult { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// + /// # let handle = /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// @@ -58,6 +59,8 @@ impl WaitTimeoutResult { /// break /// } /// } + /// # // Prevent leaks for Miri. + /// # let _ = handle.join(); /// ``` #[must_use] #[stable(feature = "wait_timeout", since = "1.5.0")] @@ -88,7 +91,7 @@ impl WaitTimeoutResult { /// let pair2 = Arc::clone(&pair); /// /// // Inside of our lock, spawn a new thread, and then wait for it to start. -/// thread::spawn(move|| { +/// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -166,7 +169,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -221,7 +224,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(true), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut pending = lock.lock().unwrap(); /// *pending = false; @@ -280,7 +283,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -352,7 +355,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -420,7 +423,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(true), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut pending = lock.lock().unwrap(); /// *pending = false; @@ -484,7 +487,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; @@ -524,7 +527,7 @@ impl Condvar { /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = Arc::clone(&pair); /// - /// thread::spawn(move|| { + /// thread::spawn(move || { /// let (lock, cvar) = &*pair2; /// let mut started = lock.lock().unwrap(); /// *started = true; diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 27b59cfc8c24d..953aef40e7b76 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -1,3 +1,4 @@ +use super::once::ExclusiveState; use crate::cell::UnsafeCell; use crate::mem::ManuallyDrop; use crate::ops::Deref; @@ -5,8 +6,6 @@ use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sync::Once; use crate::{fmt, ptr}; -use super::once::ExclusiveState; - // We use the state of a Once as discriminant value. Upon creation, the state is // "incomplete" and `f` contains the initialization closure. In the first call to // `call_once`, `f` is taken and run. If it succeeds, `value` is set and the state @@ -29,40 +28,28 @@ union Data { /// # Examples /// /// Initialize static variables with `LazyLock`. -/// /// ``` -/// #![feature(lazy_cell)] -/// -/// use std::collections::HashMap; -/// /// use std::sync::LazyLock; /// -/// static HASHMAP: LazyLock> = LazyLock::new(|| { -/// println!("initializing"); -/// let mut m = HashMap::new(); -/// m.insert(13, "Spica".to_string()); -/// m.insert(74, "Hoyten".to_string()); -/// m +/// // n.b. static items do not call [`Drop`] on program termination, so this won't be deallocated. +/// // this is fine, as the OS can deallocate the terminated program faster than we can free memory +/// // but tools like valgrind might report "memory leaks" as it isn't obvious this is intentional. +/// static DEEP_THOUGHT: LazyLock = LazyLock::new(|| { +/// # mod another_crate { +/// # pub fn great_question() -> String { "42".to_string() } +/// # } +/// // M3 Ultra takes about 16 million years in --release config +/// another_crate::great_question() /// }); /// -/// fn main() { -/// println!("ready"); -/// std::thread::spawn(|| { -/// println!("{:?}", HASHMAP.get(&13)); -/// }).join().unwrap(); -/// println!("{:?}", HASHMAP.get(&74)); -/// -/// // Prints: -/// // ready -/// // initializing -/// // Some("Spica") -/// // Some("Hoyten") -/// } +/// // The `String` is built, stored in the `LazyLock`, and returned as `&String`. +/// let _ = &*DEEP_THOUGHT; +/// // The `String` is retrieved from the `LazyLock` and returned as `&String`. +/// let _ = &*DEEP_THOUGHT; /// ``` +/// /// Initialize fields with `LazyLock`. /// ``` -/// #![feature(lazy_cell)] -/// /// use std::sync::LazyLock; /// /// #[derive(Debug)] @@ -76,8 +63,7 @@ union Data { /// println!("{}", *data.number); /// } /// ``` - -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] pub struct LazyLock T> { once: Once, data: UnsafeCell>, @@ -85,8 +71,21 @@ pub struct LazyLock T> { impl T> LazyLock { /// Creates a new lazy value with the given initializing function. + /// + /// # Examples + /// + /// ``` + /// use std::sync::LazyLock; + /// + /// let hello = "Hello, World!".to_string(); + /// + /// let lazy = LazyLock::new(|| hello.to_uppercase()); + /// + /// assert_eq!(&*lazy, "HELLO, WORLD!"); + /// ``` #[inline] - #[unstable(feature = "lazy_cell", issue = "109736")] + #[stable(feature = "lazy_cell", since = "1.80.0")] + #[rustc_const_stable(feature = "lazy_cell", since = "1.80.0")] pub const fn new(f: F) -> LazyLock { LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) } } @@ -107,8 +106,7 @@ impl T> LazyLock { /// # Examples /// /// ``` - /// #![feature(lazy_cell)] - /// #![feature(lazy_cell_consume)] + /// #![feature(lazy_cell_into_inner)] /// /// use std::sync::LazyLock; /// @@ -119,7 +117,7 @@ impl T> LazyLock { /// assert_eq!(&*lazy, "HELLO, WORLD!"); /// assert_eq!(LazyLock::into_inner(lazy).ok(), Some("HELLO, WORLD!".to_string())); /// ``` - #[unstable(feature = "lazy_cell_consume", issue = "109736")] + #[unstable(feature = "lazy_cell_into_inner", issue = "125623")] pub fn into_inner(mut this: Self) -> Result { let state = this.once.state(); match state { @@ -145,8 +143,6 @@ impl T> LazyLock { /// # Examples /// /// ``` - /// #![feature(lazy_cell)] - /// /// use std::sync::LazyLock; /// /// let lazy = LazyLock::new(|| 92); @@ -155,7 +151,7 @@ impl T> LazyLock { /// assert_eq!(&*lazy, &92); /// ``` #[inline] - #[unstable(feature = "lazy_cell", issue = "109736")] + #[stable(feature = "lazy_cell", since = "1.80.0")] pub fn force(this: &LazyLock) -> &T { this.once.call_once(|| { // SAFETY: `call_once` only runs this closure once, ever. @@ -178,7 +174,7 @@ impl T> LazyLock { } impl LazyLock { - /// Get the inner value if it has already been initialized. + /// Gets the inner value if it has already been initialized. fn get(&self) -> Option<&T> { if self.once.is_completed() { // SAFETY: @@ -191,7 +187,7 @@ impl LazyLock { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl Drop for LazyLock { fn drop(&mut self) { match self.once.state() { @@ -204,7 +200,7 @@ impl Drop for LazyLock { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl T> Deref for LazyLock { type Target = T; @@ -219,7 +215,7 @@ impl T> Deref for LazyLock { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl Default for LazyLock { /// Creates a new lazy value using `Default` as the initializing function. #[inline] @@ -228,7 +224,7 @@ impl Default for LazyLock { } } -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl fmt::Debug for LazyLock { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_tuple("LazyLock"); @@ -242,13 +238,13 @@ impl fmt::Debug for LazyLock { // We never create a `&F` from a `&LazyLock` so it is fine // to not impl `Sync` for `F`. -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] unsafe impl Sync for LazyLock {} // auto-derived `Send` impl is OK. -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl RefUnwindSafe for LazyLock {} -#[unstable(feature = "lazy_cell", issue = "109736")] +#[stable(feature = "lazy_cell", since = "1.80.0")] impl UnwindSafe for LazyLock {} #[cfg(test)] diff --git a/library/std/src/sync/lazy_lock/tests.rs b/library/std/src/sync/lazy_lock/tests.rs index a5d4e25c5962a..8a6ab4ac4fd99 100644 --- a/library/std/src/sync/lazy_lock/tests.rs +++ b/library/std/src/sync/lazy_lock/tests.rs @@ -1,13 +1,8 @@ -use crate::{ - cell::LazyCell, - panic, - sync::{ - atomic::{AtomicUsize, Ordering::SeqCst}, - Mutex, - }, - sync::{LazyLock, OnceLock}, - thread, -}; +use crate::cell::LazyCell; +use crate::sync::atomic::AtomicUsize; +use crate::sync::atomic::Ordering::SeqCst; +use crate::sync::{LazyLock, Mutex, OnceLock}; +use crate::{panic, thread}; fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { thread::spawn(f).join().unwrap() diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index e8c35bd48a70b..d0ba8cc3b47df 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -133,10 +133,14 @@ //! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at //! most one thread at a time is able to access some data. //! -//! - [`Once`]: Used for a thread-safe, one-time global initialization routine +//! - [`Once`]: Used for a thread-safe, one-time global initialization routine. +//! Mostly useful for implementing other types like `OnceLock`. //! //! - [`OnceLock`]: Used for thread-safe, one-time initialization of a -//! global variable. +//! variable, with potentially different initializers based on the caller. +//! +//! - [`LazyLock`]: Used for thread-safe, one-time initialization of a +//! variable, using one nullary initializer function provided at creation. //! //! - [`RwLock`]: Provides a mutual exclusion mechanism which allows //! multiple readers at the same time, while allowing only one @@ -154,17 +158,20 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::sync::{Arc, Weak}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::sync::atomic; #[unstable(feature = "exclusive_wrapper", issue = "98407")] pub use core::sync::Exclusive; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::sync::{Arc, Weak}; + #[stable(feature = "rust1", since = "1.0.0")] pub use self::barrier::{Barrier, BarrierWaitResult}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::condvar::{Condvar, WaitTimeoutResult}; +#[stable(feature = "lazy_cell", since = "1.80.0")] +pub use self::lazy_lock::LazyLock; #[unstable(feature = "mapped_lock_guards", issue = "117108")] pub use self::mutex::MappedMutexGuard; #[stable(feature = "rust1", since = "1.0.0")] @@ -172,21 +179,17 @@ pub use self::mutex::{Mutex, MutexGuard}; #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated)] pub use self::once::{Once, OnceState, ONCE_INIT}; +#[stable(feature = "once_cell", since = "1.70.0")] +pub use self::once_lock::OnceLock; #[stable(feature = "rust1", since = "1.0.0")] pub use self::poison::{LockResult, PoisonError, TryLockError, TryLockResult}; +#[unstable(feature = "reentrant_lock", issue = "121440")] +pub use self::reentrant_lock::{ReentrantLock, ReentrantLockGuard}; #[unstable(feature = "mapped_lock_guards", issue = "117108")] pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; -#[unstable(feature = "lazy_cell", issue = "109736")] -pub use self::lazy_lock::LazyLock; -#[stable(feature = "once_cell", since = "1.70.0")] -pub use self::once_lock::OnceLock; - -#[unstable(feature = "reentrant_lock", issue = "121440")] -pub use self::reentrant_lock::{ReentrantLock, ReentrantLockGuard}; - pub mod mpsc; mod barrier; diff --git a/library/std/src/sync/mpmc/array.rs b/library/std/src/sync/mpmc/array.rs index 492e21d9bdb63..34acd9c9a943b 100644 --- a/library/std/src/sync/mpmc/array.rs +++ b/library/std/src/sync/mpmc/array.rs @@ -13,7 +13,6 @@ use super::error::*; use super::select::{Operation, Selected, Token}; use super::utils::{Backoff, CachePadded}; use super::waker::SyncWaker; - use crate::cell::UnsafeCell; use crate::mem::MaybeUninit; use crate::ptr; @@ -200,11 +199,12 @@ impl Channel { return Err(msg); } - let slot: &Slot = &*(token.array.slot as *const Slot); - // Write the message into the slot and update the stamp. - slot.msg.get().write(MaybeUninit::new(msg)); - slot.stamp.store(token.array.stamp, Ordering::Release); + unsafe { + let slot: &Slot = &*(token.array.slot as *const Slot); + slot.msg.get().write(MaybeUninit::new(msg)); + slot.stamp.store(token.array.stamp, Ordering::Release); + } // Wake a sleeping receiver. self.receivers.notify(); @@ -291,11 +291,14 @@ impl Channel { return Err(()); } - let slot: &Slot = &*(token.array.slot as *const Slot); - // Read the message from the slot and update the stamp. - let msg = slot.msg.get().read().assume_init(); - slot.stamp.store(token.array.stamp, Ordering::Release); + let msg = unsafe { + let slot: &Slot = &*(token.array.slot as *const Slot); + + let msg = slot.msg.get().read().assume_init(); + slot.stamp.store(token.array.stamp, Ordering::Release); + msg + }; // Wake a sleeping sender. self.senders.notify(); @@ -471,7 +474,7 @@ impl Channel { false }; - self.discard_all_messages(tail); + unsafe { self.discard_all_messages(tail) }; disconnected } diff --git a/library/std/src/sync/mpmc/context.rs b/library/std/src/sync/mpmc/context.rs index bbfc6ce00ffc2..8db3c9896eb77 100644 --- a/library/std/src/sync/mpmc/context.rs +++ b/library/std/src/sync/mpmc/context.rs @@ -2,7 +2,6 @@ use super::select::Selected; use super::waker::current_thread_id; - use crate::cell::Cell; use crate::ptr; use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; diff --git a/library/std/src/sync/mpmc/counter.rs b/library/std/src/sync/mpmc/counter.rs index a5a6bdc67f13f..d1bfe612f536f 100644 --- a/library/std/src/sync/mpmc/counter.rs +++ b/library/std/src/sync/mpmc/counter.rs @@ -1,6 +1,5 @@ -use crate::ops; -use crate::process; use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use crate::{ops, process}; /// Reference counter internals. struct Counter { @@ -63,7 +62,7 @@ impl Sender { disconnect(&self.counter().chan); if self.counter().destroy.swap(true, Ordering::AcqRel) { - drop(Box::from_raw(self.counter)); + drop(unsafe { Box::from_raw(self.counter) }); } } } @@ -116,7 +115,7 @@ impl Receiver { disconnect(&self.counter().chan); if self.counter().destroy.swap(true, Ordering::AcqRel) { - drop(Box::from_raw(self.counter)); + drop(unsafe { Box::from_raw(self.counter) }); } } } diff --git a/library/std/src/sync/mpmc/error.rs b/library/std/src/sync/mpmc/error.rs index 33b2bff853498..e3aec7e76232f 100644 --- a/library/std/src/sync/mpmc/error.rs +++ b/library/std/src/sync/mpmc/error.rs @@ -1,7 +1,5 @@ -use crate::error; -use crate::fmt; - pub use crate::sync::mpsc::{RecvError, RecvTimeoutError, SendError, TryRecvError, TrySendError}; +use crate::{error, fmt}; /// An error returned from the [`send_timeout`] method. /// diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs index 9e7148c716cda..bbe205cad04e6 100644 --- a/library/std/src/sync/mpmc/list.rs +++ b/library/std/src/sync/mpmc/list.rs @@ -5,7 +5,6 @@ use super::error::*; use super::select::{Operation, Selected, Token}; use super::utils::{Backoff, CachePadded}; use super::waker::SyncWaker; - use crate::cell::UnsafeCell; use crate::marker::PhantomData; use crate::mem::MaybeUninit; @@ -91,7 +90,7 @@ impl Block { // It is not necessary to set the `DESTROY` bit in the last slot because that slot has // begun destruction of the block. for i in start..BLOCK_CAP - 1 { - let slot = (*this).slots.get_unchecked(i); + let slot = unsafe { (*this).slots.get_unchecked(i) }; // Mark the `DESTROY` bit if a thread is still using the slot. if slot.state.load(Ordering::Acquire) & READ == 0 @@ -103,7 +102,7 @@ impl Block { } // No thread is using the block, now it is safe to destroy it. - drop(Box::from_raw(this)); + drop(unsafe { Box::from_raw(this) }); } } @@ -265,9 +264,11 @@ impl Channel { // Write the message into the slot. let block = token.list.block as *mut Block; let offset = token.list.offset; - let slot = (*block).slots.get_unchecked(offset); - slot.msg.get().write(MaybeUninit::new(msg)); - slot.state.fetch_or(WRITE, Ordering::Release); + unsafe { + let slot = (*block).slots.get_unchecked(offset); + slot.msg.get().write(MaybeUninit::new(msg)); + slot.state.fetch_or(WRITE, Ordering::Release); + } // Wake a sleeping receiver. self.receivers.notify(); @@ -369,19 +370,21 @@ impl Channel { // Read the message. let block = token.list.block as *mut Block; let offset = token.list.offset; - let slot = (*block).slots.get_unchecked(offset); - slot.wait_write(); - let msg = slot.msg.get().read().assume_init(); - - // Destroy the block if we've reached the end, or if another thread wanted to destroy but - // couldn't because we were busy reading from the slot. - if offset + 1 == BLOCK_CAP { - Block::destroy(block, 0); - } else if slot.state.fetch_or(READ, Ordering::AcqRel) & DESTROY != 0 { - Block::destroy(block, offset + 1); - } + unsafe { + let slot = (*block).slots.get_unchecked(offset); + slot.wait_write(); + let msg = slot.msg.get().read().assume_init(); + + // Destroy the block if we've reached the end, or if another thread wanted to destroy but + // couldn't because we were busy reading from the slot. + if offset + 1 == BLOCK_CAP { + Block::destroy(block, 0); + } else if slot.state.fetch_or(READ, Ordering::AcqRel) & DESTROY != 0 { + Block::destroy(block, offset + 1); + } - Ok(msg) + Ok(msg) + } } /// Attempts to send a message into the channel. diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index 2068dda393a2b..c640e07348ea0 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -40,10 +40,11 @@ mod utils; mod waker; mod zero; +pub use error::*; + use crate::fmt; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::time::{Duration, Instant}; -pub use error::*; /// Creates a channel of unbounded capacity. /// diff --git a/library/std/src/sync/mpmc/waker.rs b/library/std/src/sync/mpmc/waker.rs index 9aab1b9417edb..fb877887f9c9d 100644 --- a/library/std/src/sync/mpmc/waker.rs +++ b/library/std/src/sync/mpmc/waker.rs @@ -2,7 +2,6 @@ use super::context::Context; use super::select::{Operation, Selected}; - use crate::ptr; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::Mutex; diff --git a/library/std/src/sync/mpmc/zero.rs b/library/std/src/sync/mpmc/zero.rs index 1b82713edc748..2b82eeda3d5fb 100644 --- a/library/std/src/sync/mpmc/zero.rs +++ b/library/std/src/sync/mpmc/zero.rs @@ -7,7 +7,6 @@ use super::error::*; use super::select::{Operation, Selected, Token}; use super::utils::Backoff; use super::waker::Waker; - use crate::cell::UnsafeCell; use crate::marker::PhantomData; use crate::sync::atomic::{AtomicBool, Ordering}; @@ -103,9 +102,11 @@ impl Channel { return Err(msg); } - let packet = &*(token.zero.0 as *const Packet); - packet.msg.get().write(Some(msg)); - packet.ready.store(true, Ordering::Release); + unsafe { + let packet = &*(token.zero.0 as *const Packet); + packet.msg.get().write(Some(msg)); + packet.ready.store(true, Ordering::Release); + } Ok(()) } @@ -116,22 +117,24 @@ impl Channel { return Err(()); } - let packet = &*(token.zero.0 as *const Packet); + let packet = unsafe { &*(token.zero.0 as *const Packet) }; if packet.on_stack { // The message has been in the packet from the beginning, so there is no need to wait // for it. However, after reading the message, we need to set `ready` to `true` in // order to signal that the packet can be destroyed. - let msg = packet.msg.get().replace(None).unwrap(); + let msg = unsafe { packet.msg.get().replace(None) }.unwrap(); packet.ready.store(true, Ordering::Release); Ok(msg) } else { // Wait until the message becomes available, then read it and destroy the // heap-allocated packet. packet.wait_ready(); - let msg = packet.msg.get().replace(None).unwrap(); - drop(Box::from_raw(token.zero.0 as *mut Packet)); - Ok(msg) + unsafe { + let msg = packet.msg.get().replace(None).unwrap(); + drop(Box::from_raw(token.zero.0 as *mut Packet)); + Ok(msg) + } } } diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index d353c7bd5de9e..26d5b9515a244 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -51,7 +51,7 @@ //! //! // Create a simple streaming channel //! let (tx, rx) = channel(); -//! thread::spawn(move|| { +//! thread::spawn(move || { //! tx.send(10).unwrap(); //! }); //! assert_eq!(rx.recv().unwrap(), 10); @@ -69,7 +69,7 @@ //! let (tx, rx) = channel(); //! for i in 0..10 { //! let tx = tx.clone(); -//! thread::spawn(move|| { +//! thread::spawn(move || { //! tx.send(i).unwrap(); //! }); //! } @@ -99,7 +99,7 @@ //! use std::sync::mpsc::sync_channel; //! //! let (tx, rx) = sync_channel::(0); -//! thread::spawn(move|| { +//! thread::spawn(move || { //! // This will wait for the parent thread to start receiving //! tx.send(53).unwrap(); //! }); @@ -148,10 +148,9 @@ mod sync_tests; // not exposed publicly, but if you are curious about the implementation, // that's where everything is. -use crate::error; -use crate::fmt; use crate::sync::mpmc; use crate::time::{Duration, Instant}; +use crate::{error, fmt}; /// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. /// This half can only be owned by one thread. @@ -510,7 +509,7 @@ pub enum TrySendError { /// let (sender, receiver) = channel(); /// /// // Spawn off an expensive computation -/// thread::spawn(move|| { +/// thread::spawn(move || { /// # fn expensive_computation() {} /// sender.send(expensive_computation()).unwrap(); /// }); @@ -561,7 +560,7 @@ pub fn channel() -> (Sender, Receiver) { /// // this returns immediately /// sender.send(1).unwrap(); /// -/// thread::spawn(move|| { +/// thread::spawn(move || { /// // this will block until the previous message has been received /// sender.send(2).unwrap(); /// }); diff --git a/library/std/src/sync/mpsc/sync_tests.rs b/library/std/src/sync/mpsc/sync_tests.rs index 945de280f40d8..49b65c8efe692 100644 --- a/library/std/src/sync/mpsc/sync_tests.rs +++ b/library/std/src/sync/mpsc/sync_tests.rs @@ -1,8 +1,7 @@ use super::*; -use crate::env; use crate::rc::Rc; use crate::sync::mpmc::SendTimeoutError; -use crate::thread; +use crate::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/library/std/src/sync/mpsc/tests.rs b/library/std/src/sync/mpsc/tests.rs index ac1a804cf9c84..13892fa0d18e4 100644 --- a/library/std/src/sync/mpsc/tests.rs +++ b/library/std/src/sync/mpsc/tests.rs @@ -1,6 +1,5 @@ use super::*; -use crate::env; -use crate::thread; +use crate::{env, thread}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 608229fd674d8..bf595fdea2d25 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -10,9 +10,15 @@ use crate::fmt; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sys::sync as sys; -/// A synchronization primitive which can be used to run a one-time global -/// initialization. Useful for one-time initialization for FFI or related -/// functionality. This type can only be constructed with [`Once::new()`]. +/// A low-level synchronization primitive for one-time global execution. +/// +/// Previously this was the only "execute once" synchronization in `std`. +/// Other libraries implemented novel synchronizing types with `Once`, like +/// [`OnceLock`] or [`LazyLock`], before those were added to `std`. +/// `OnceLock` in particular supersedes `Once` in functionality and should +/// be preferred for the common case where the `Once` is associated with data. +/// +/// This type can only be constructed with [`Once::new()`]. /// /// # Examples /// @@ -25,6 +31,9 @@ use crate::sys::sync as sys; /// // run initialization here /// }); /// ``` +/// +/// [`OnceLock`]: crate::sync::OnceLock +/// [`LazyLock`]: crate::sync::LazyLock #[stable(feature = "rust1", since = "1.0.0")] pub struct Once { inner: sys::Once, @@ -61,7 +70,7 @@ pub(crate) enum ExclusiveState { #[stable(feature = "rust1", since = "1.0.0")] #[deprecated( since = "1.38.0", - note = "the `new` function is now preferred", + note = "the `Once::new()` function is now preferred", suggestion = "Once::new()" )] pub const ONCE_INIT: Once = Once::new(); @@ -255,6 +264,47 @@ impl Once { self.inner.is_completed() } + /// Blocks the current thread until initialization has completed. + /// + /// # Example + /// + /// ```rust + /// #![feature(once_wait)] + /// + /// use std::sync::Once; + /// use std::thread; + /// + /// static READY: Once = Once::new(); + /// + /// let thread = thread::spawn(|| { + /// READY.wait(); + /// println!("everything is ready"); + /// }); + /// + /// READY.call_once(|| println!("performing setup")); + /// ``` + /// + /// # Panics + /// + /// If this [`Once`] has been poisoned because an initialization closure has + /// panicked, this method will also panic. Use [`wait_force`](Self::wait_force) + /// if this behaviour is not desired. + #[unstable(feature = "once_wait", issue = "127527")] + pub fn wait(&self) { + if !self.inner.is_completed() { + self.inner.wait(false); + } + } + + /// Blocks the current thread until initialization has completed, ignoring + /// poisoning. + #[unstable(feature = "once_wait", issue = "127527")] + pub fn wait_force(&self) { + if !self.inner.is_completed() { + self.inner.wait(true); + } + } + /// Returns the current state of the `Once` instance. /// /// Since this takes a mutable reference, no initialization can currently diff --git a/library/std/src/sync/once/tests.rs b/library/std/src/sync/once/tests.rs index 0c35597e11c51..ce96468aeb6e1 100644 --- a/library/std/src/sync/once/tests.rs +++ b/library/std/src/sync/once/tests.rs @@ -1,7 +1,9 @@ use super::Once; -use crate::panic; +use crate::sync::atomic::AtomicBool; +use crate::sync::atomic::Ordering::Relaxed; use crate::sync::mpsc::channel; -use crate::thread; +use crate::time::Duration; +use crate::{panic, thread}; #[test] fn smoke_once() { @@ -114,3 +116,47 @@ fn wait_for_force_to_finish() { assert!(t1.join().is_ok()); assert!(t2.join().is_ok()); } + +#[test] +fn wait() { + for _ in 0..50 { + let val = AtomicBool::new(false); + let once = Once::new(); + + thread::scope(|s| { + for _ in 0..4 { + s.spawn(|| { + once.wait(); + assert!(val.load(Relaxed)); + }); + } + + once.call_once(|| val.store(true, Relaxed)); + }); + } +} + +#[test] +fn wait_on_poisoned() { + let once = Once::new(); + + panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err(); + panic::catch_unwind(|| once.wait()).unwrap_err(); +} + +#[test] +fn wait_force_on_poisoned() { + let once = Once::new(); + + thread::scope(|s| { + panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err(); + + s.spawn(|| { + thread::sleep(Duration::from_millis(100)); + + once.call_once_force(|_| {}); + }); + + once.wait_force(); + }) +} diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index fc830baccedd2..56cf877ddc6d5 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -5,50 +5,20 @@ use crate::mem::MaybeUninit; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sync::Once; -/// A synchronization primitive which can be written to only once. +/// A synchronization primitive which can nominally be written to only once. /// /// This type is a thread-safe [`OnceCell`], and can be used in statics. +/// In many simple cases, you can use [`LazyLock`] instead to get the benefits of this type +/// with less effort: `LazyLock` "looks like" `&T` because it initializes with `F` on deref! +/// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock +/// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`]. /// /// [`OnceCell`]: crate::cell::OnceCell +/// [`LazyLock`]: crate::sync::LazyLock +/// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new /// /// # Examples /// -/// Using `OnceLock` to store a function’s previously computed value (a.k.a. -/// ‘lazy static’ or ‘memoizing’): -/// -/// ``` -/// use std::sync::OnceLock; -/// -/// struct DeepThought { -/// answer: String, -/// } -/// -/// impl DeepThought { -/// # fn great_question() -> String { -/// # "42".to_string() -/// # } -/// # -/// fn new() -> Self { -/// Self { -/// // M3 Ultra takes about 16 million years in --release config -/// answer: Self::great_question(), -/// } -/// } -/// } -/// -/// fn computation() -> &'static DeepThought { -/// // n.b. static items do not call [`Drop`] on program termination, so if -/// // [`DeepThought`] impls Drop, that will not be used for this instance. -/// static COMPUTATION: OnceLock = OnceLock::new(); -/// COMPUTATION.get_or_init(|| DeepThought::new()) -/// } -/// -/// // The `DeepThought` is built, stored in the `OnceLock`, and returned. -/// let _ = computation().answer; -/// // The `DeepThought` is retrieved from the `OnceLock` and returned. -/// let _ = computation().answer; -/// ``` -/// /// Writing to a `OnceLock` from a separate thread: /// /// ``` @@ -73,6 +43,62 @@ use crate::sync::Once; /// Some(&12345), /// ); /// ``` +/// +/// You can use `OnceLock` to implement a type that requires "append-only" logic: +/// +/// ``` +/// use std::sync::{OnceLock, atomic::{AtomicU32, Ordering}}; +/// use std::thread; +/// +/// struct OnceList { +/// data: OnceLock, +/// next: OnceLock>>, +/// } +/// impl OnceList { +/// const fn new() -> OnceList { +/// OnceList { data: OnceLock::new(), next: OnceLock::new() } +/// } +/// fn push(&self, value: T) { +/// // FIXME: this impl is concise, but is also slow for long lists or many threads. +/// // as an exercise, consider how you might improve on it while preserving the behavior +/// if let Err(value) = self.data.set(value) { +/// let next = self.next.get_or_init(|| Box::new(OnceList::new())); +/// next.push(value) +/// }; +/// } +/// fn contains(&self, example: &T) -> bool +/// where +/// T: PartialEq, +/// { +/// self.data.get().map(|item| item == example).filter(|v| *v).unwrap_or_else(|| { +/// self.next.get().map(|next| next.contains(example)).unwrap_or(false) +/// }) +/// } +/// } +/// +/// // Let's exercise this new Sync append-only list by doing a little counting +/// static LIST: OnceList = OnceList::new(); +/// static COUNTER: AtomicU32 = AtomicU32::new(0); +/// +/// # const LEN: u32 = if cfg!(miri) { 50 } else { 1000 }; +/// # /* +/// const LEN: u32 = 1000; +/// # */ +/// thread::scope(|s| { +/// for _ in 0..thread::available_parallelism().unwrap().get() { +/// s.spawn(|| { +/// while let i @ 0..LEN = COUNTER.fetch_add(1, Ordering::Relaxed) { +/// LIST.push(i); +/// } +/// }); +/// } +/// }); +/// +/// for i in 0..LEN { +/// assert!(LIST.contains(&i)); +/// } +/// +/// ``` #[stable(feature = "once_cell", since = "1.70.0")] pub struct OnceLock { once: Once, @@ -141,6 +167,34 @@ impl OnceLock { } } + /// Blocks the current thread until the cell is initialized. + /// + /// # Example + /// + /// Waiting for a computation on another thread to finish: + /// ```rust + /// #![feature(once_wait)] + /// + /// use std::thread; + /// use std::sync::OnceLock; + /// + /// let value = OnceLock::new(); + /// + /// thread::scope(|s| { + /// s.spawn(|| value.set(1 + 1)); + /// + /// let result = value.wait(); + /// assert_eq!(result, &2); + /// }) + /// ``` + #[inline] + #[unstable(feature = "once_wait", issue = "127527")] + pub fn wait(&self) -> &T { + self.once.wait_force(); + + unsafe { self.get_unchecked() } + } + /// Sets the contents of this cell to `value`. /// /// May block if another thread is currently attempting to initialize the cell. The cell is @@ -255,9 +309,7 @@ impl OnceLock { /// Gets the mutable reference of the contents of the cell, initializing /// it with `f` if the cell was empty. /// - /// Many threads may call `get_mut_or_init` concurrently with different - /// initializing functions, but it is guaranteed that only one function - /// will be executed. + /// This method never blocks. /// /// # Panics /// @@ -347,6 +399,8 @@ impl OnceLock { /// it with `f` if the cell was empty. If the cell was empty and `f` failed, /// an error is returned. /// + /// This method never blocks. + /// /// # Panics /// /// If `f` panics, the panic is propagated to the caller, and @@ -476,7 +530,7 @@ impl OnceLock { #[inline] unsafe fn get_unchecked(&self) -> &T { debug_assert!(self.is_initialized()); - (&*self.value.get()).assume_init_ref() + unsafe { (&*self.value.get()).assume_init_ref() } } /// # Safety @@ -485,7 +539,7 @@ impl OnceLock { #[inline] unsafe fn get_unchecked_mut(&mut self) -> &mut T { debug_assert!(self.is_initialized()); - (&mut *self.value.get()).assume_init_mut() + unsafe { (&mut *self.value.get()).assume_init_mut() } } } @@ -552,7 +606,7 @@ impl Clone for OnceLock { #[stable(feature = "once_cell", since = "1.70.0")] impl From for OnceLock { - /// Create a new cell with its contents set to `value`. + /// Creates a new cell with its contents set to `value`. /// /// # Example /// diff --git a/library/std/src/sync/once_lock/tests.rs b/library/std/src/sync/once_lock/tests.rs index d5d32e73d8880..176830c6748b2 100644 --- a/library/std/src/sync/once_lock/tests.rs +++ b/library/std/src/sync/once_lock/tests.rs @@ -1,12 +1,8 @@ -use crate::{ - panic, - sync::OnceLock, - sync::{ - atomic::{AtomicUsize, Ordering::SeqCst}, - mpsc::channel, - }, - thread, -}; +use crate::sync::atomic::AtomicUsize; +use crate::sync::atomic::Ordering::SeqCst; +use crate::sync::mpsc::channel; +use crate::sync::OnceLock; +use crate::{panic, thread}; fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { thread::spawn(f).join().unwrap() diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index f4975088b372d..da66a088e51b1 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -1,6 +1,5 @@ use crate::error::Error; use crate::fmt; - #[cfg(panic = "unwind")] use crate::sync::atomic::{AtomicBool, Ordering}; #[cfg(panic = "unwind")] @@ -31,13 +30,13 @@ impl Flag { } } - /// Check the flag for an unguarded borrow, where we only care about existing poison. + /// Checks the flag for an unguarded borrow, where we only care about existing poison. #[inline] pub fn borrow(&self) -> LockResult<()> { if self.get() { Err(PoisonError::new(())) } else { Ok(()) } } - /// Check the flag for a guarded borrow, where we may also set poison when `done`. + /// Checks the flag for a guarded borrow, where we may also set poison when `done`. #[inline] pub fn guard(&self) -> LockResult { let ret = Guard { diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs index 80b9e0cf15214..84a0b36db1798 100644 --- a/library/std/src/sync/reentrant_lock.rs +++ b/library/std/src/sync/reentrant_lock.rs @@ -1,12 +1,14 @@ #[cfg(all(test, not(target_os = "emscripten")))] mod tests; +use cfg_if::cfg_if; + use crate::cell::UnsafeCell; use crate::fmt; use crate::ops::Deref; use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed}; use crate::sys::sync as sys; +use crate::thread::{current_id, ThreadId}; /// A re-entrant mutual exclusion lock /// @@ -53,8 +55,8 @@ use crate::sys::sync as sys; // // The 'owner' field tracks which thread has locked the mutex. // -// We use current_thread_unique_ptr() as the thread identifier, -// which is just the address of a thread local variable. +// We use thread::current_id() as the thread identifier, which is just the +// current thread's ThreadId, so it's unique across the process lifetime. // // If `owner` is set to the identifier of the current thread, // we assume the mutex is already locked and instead of locking it again, @@ -72,14 +74,109 @@ use crate::sys::sync as sys; // since we're not dealing with multiple threads. If it's not equal, // synchronization is left to the mutex, making relaxed memory ordering for // the `owner` field fine in all cases. +// +// On systems without 64 bit atomics we also store the address of a TLS variable +// along the 64-bit TID. We then first check that address against the address +// of that variable on the current thread, and only if they compare equal do we +// compare the actual TIDs. Because we only ever read the TID on the same thread +// that it was written on (or a thread sharing the TLS block with that writer thread), +// we don't need to further synchronize the TID accesses, so they can be regular 64-bit +// non-atomic accesses. #[unstable(feature = "reentrant_lock", issue = "121440")] pub struct ReentrantLock { mutex: sys::Mutex, - owner: AtomicUsize, + owner: Tid, lock_count: UnsafeCell, data: T, } +cfg_if!( + if #[cfg(target_has_atomic = "64")] { + use crate::sync::atomic::{AtomicU64, Ordering::Relaxed}; + + struct Tid(AtomicU64); + + impl Tid { + const fn new() -> Self { + Self(AtomicU64::new(0)) + } + + #[inline] + fn contains(&self, owner: ThreadId) -> bool { + owner.as_u64().get() == self.0.load(Relaxed) + } + + #[inline] + // This is just unsafe to match the API of the Tid type below. + unsafe fn set(&self, tid: Option) { + let value = tid.map_or(0, |tid| tid.as_u64().get()); + self.0.store(value, Relaxed); + } + } + } else { + /// Returns the address of a TLS variable. This is guaranteed to + /// be unique across all currently alive threads. + fn tls_addr() -> usize { + thread_local! { static X: u8 = const { 0u8 } }; + + X.with(|p| <*const u8>::addr(p)) + } + + use crate::sync::atomic::{ + AtomicUsize, + Ordering, + }; + + struct Tid { + // When a thread calls `set()`, this value gets updated to + // the address of a thread local on that thread. This is + // used as a first check in `contains()`; if the `tls_addr` + // doesn't match the TLS address of the current thread, then + // the ThreadId also can't match. Only if the TLS addresses do + // match do we read out the actual TID. + // Note also that we can use relaxed atomic operations here, because + // we only ever read from the tid if `tls_addr` matches the current + // TLS address. In that case, either the the tid has been set by + // the current thread, or by a thread that has terminated before + // the current thread was created. In either case, no further + // synchronization is needed (as per ) + tls_addr: AtomicUsize, + tid: UnsafeCell, + } + + unsafe impl Send for Tid {} + unsafe impl Sync for Tid {} + + impl Tid { + const fn new() -> Self { + Self { tls_addr: AtomicUsize::new(0), tid: UnsafeCell::new(0) } + } + + #[inline] + // NOTE: This assumes that `owner` is the ID of the current + // thread, and may spuriously return `false` if that's not the case. + fn contains(&self, owner: ThreadId) -> bool { + // SAFETY: See the comments in the struct definition. + self.tls_addr.load(Ordering::Relaxed) == tls_addr() + && unsafe { *self.tid.get() } == owner.as_u64().get() + } + + #[inline] + // This may only be called by one thread at a time, and can lead to + // race conditions otherwise. + unsafe fn set(&self, tid: Option) { + // It's important that we set `self.tls_addr` to 0 if the tid is + // cleared. Otherwise, there might be race conditions between + // `set()` and `get()`. + let tls_addr = if tid.is_some() { tls_addr() } else { 0 }; + let value = tid.map_or(0, |tid| tid.as_u64().get()); + self.tls_addr.store(tls_addr, Ordering::Relaxed); + unsafe { *self.tid.get() = value }; + } + } + } +); + #[unstable(feature = "reentrant_lock", issue = "121440")] unsafe impl Send for ReentrantLock {} #[unstable(feature = "reentrant_lock", issue = "121440")] @@ -116,6 +213,9 @@ pub struct ReentrantLockGuard<'a, T: ?Sized + 'a> { #[unstable(feature = "reentrant_lock", issue = "121440")] impl !Send for ReentrantLockGuard<'_, T> {} +#[unstable(feature = "reentrant_lock", issue = "121440")] +unsafe impl Sync for ReentrantLockGuard<'_, T> {} + #[unstable(feature = "reentrant_lock", issue = "121440")] impl ReentrantLock { /// Creates a new re-entrant lock in an unlocked state ready for use. @@ -131,7 +231,7 @@ impl ReentrantLock { pub const fn new(t: T) -> ReentrantLock { ReentrantLock { mutex: sys::Mutex::new(), - owner: AtomicUsize::new(0), + owner: Tid::new(), lock_count: UnsafeCell::new(0), data: t, } @@ -181,14 +281,16 @@ impl ReentrantLock { /// assert_eq!(lock.lock().get(), 10); /// ``` pub fn lock(&self) -> ReentrantLockGuard<'_, T> { - let this_thread = current_thread_unique_ptr(); - // Safety: We only touch lock_count when we own the lock. + let this_thread = current_id(); + // Safety: We only touch lock_count when we own the inner mutex. + // Additionally, we only call `self.owner.set()` while holding + // the inner mutex, so no two threads can call it concurrently. unsafe { - if self.owner.load(Relaxed) == this_thread { + if self.owner.contains(this_thread) { self.increment_lock_count().expect("lock count overflow in reentrant mutex"); } else { self.mutex.lock(); - self.owner.store(this_thread, Relaxed); + self.owner.set(Some(this_thread)); debug_assert_eq!(*self.lock_count.get(), 0); *self.lock_count.get() = 1; } @@ -223,14 +325,16 @@ impl ReentrantLock { /// /// This function does not block. pub(crate) fn try_lock(&self) -> Option> { - let this_thread = current_thread_unique_ptr(); - // Safety: We only touch lock_count when we own the lock. + let this_thread = current_id(); + // Safety: We only touch lock_count when we own the inner mutex. + // Additionally, we only call `self.owner.set()` while holding + // the inner mutex, so no two threads can call it concurrently. unsafe { - if self.owner.load(Relaxed) == this_thread { + if self.owner.contains(this_thread) { self.increment_lock_count()?; Some(ReentrantLockGuard { lock: self }) } else if self.mutex.try_lock() { - self.owner.store(this_thread, Relaxed); + self.owner.set(Some(this_thread)); debug_assert_eq!(*self.lock_count.get(), 0); *self.lock_count.get() = 1; Some(ReentrantLockGuard { lock: self }) @@ -241,7 +345,9 @@ impl ReentrantLock { } unsafe fn increment_lock_count(&self) -> Option<()> { - *self.lock_count.get() = (*self.lock_count.get()).checked_add(1)?; + unsafe { + *self.lock_count.get() = (*self.lock_count.get()).checked_add(1)?; + } Some(()) } } @@ -303,18 +409,9 @@ impl Drop for ReentrantLockGuard<'_, T> { unsafe { *self.lock.lock_count.get() -= 1; if *self.lock.lock_count.get() == 0 { - self.lock.owner.store(0, Relaxed); + self.lock.owner.set(None); self.lock.mutex.unlock(); } } } } - -/// Get an address that is unique per running thread. -/// -/// This can be used as a non-null usize-sized ID. -pub(crate) fn current_thread_unique_ptr() -> usize { - // Use a non-drop type to make sure it's still available during thread destruction. - thread_local! { static X: u8 = const { 0 } } - X.with(|x| <*const _>::addr(x)) -} diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index e0a8a7603d71a..d995a16e056d6 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -573,19 +573,19 @@ impl From for RwLock { } impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { - /// Create a new instance of `RwLockReadGuard` from a `RwLock`. + /// Creates a new instance of `RwLockReadGuard` from a `RwLock`. // SAFETY: if and only if `lock.inner.read()` (or `lock.inner.try_read()`) has been // successfully called from the same thread before instantiating this object. unsafe fn new(lock: &'rwlock RwLock) -> LockResult> { poison::map_result(lock.poison.borrow(), |()| RwLockReadGuard { - data: NonNull::new_unchecked(lock.data.get()), + data: unsafe { NonNull::new_unchecked(lock.data.get()) }, inner_lock: &lock.inner, }) } } impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { - /// Create a new instance of `RwLockWriteGuard` from a `RwLock`. + /// Creates a new instance of `RwLockWriteGuard` from a `RwLock`. // SAFETY: if and only if `lock.inner.write()` (or `lock.inner.try_write()`) has been // successfully called from the same thread before instantiating this object. unsafe fn new(lock: &'rwlock RwLock) -> LockResult> { diff --git a/library/std/src/sync/rwlock/tests.rs b/library/std/src/sync/rwlock/tests.rs index 9cc5e7a3a60f1..12bb0fbf0503b 100644 --- a/library/std/src/sync/rwlock/tests.rs +++ b/library/std/src/sync/rwlock/tests.rs @@ -1,3 +1,5 @@ +use rand::Rng; + use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sync::mpsc::channel; use crate::sync::{ @@ -5,7 +7,6 @@ use crate::sync::{ TryLockError, }; use crate::thread; -use rand::Rng; #[derive(Eq, PartialEq, Debug)] struct NonCopy(i32); diff --git a/library/std/src/sys/anonymous_pipe/mod.rs b/library/std/src/sys/anonymous_pipe/mod.rs new file mode 100644 index 0000000000000..aa14c8b650d34 --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/mod.rs @@ -0,0 +1,14 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +cfg_if::cfg_if! { + if #[cfg(unix)] { + mod unix; + pub use unix::{AnonPipe, pipe}; + } else if #[cfg(windows)] { + mod windows; + pub use windows::{AnonPipe, pipe}; + } else { + mod unsupported; + pub use unsupported::{AnonPipe, pipe}; + } +} diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs new file mode 100644 index 0000000000000..9168024730e67 --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/unix.rs @@ -0,0 +1,102 @@ +use crate::io; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::pipe::{PipeReader, PipeWriter}; +use crate::process::Stdio; +use crate::sys::fd::FileDesc; +use crate::sys::pipe::anon_pipe; +use crate::sys_common::{FromInner, IntoInner}; + +pub type AnonPipe = FileDesc; + +#[inline] +pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsFd for PipeReader { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawFd for PipeReader { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedFd { + fn from(pipe: PipeReader) -> Self { + FileDesc::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawFd for PipeReader { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FileDesc::from_raw_fd(raw_fd)) } + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawFd for PipeReader { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeReader) -> Self { + Self::from(OwnedFd::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsFd for PipeWriter { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawFd for PipeWriter { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedFd { + fn from(pipe: PipeWriter) -> Self { + FileDesc::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawFd for PipeWriter { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FileDesc::from_raw_fd(raw_fd)) } + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawFd for PipeWriter { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeWriter) -> Self { + Self::from(OwnedFd::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeReader { + fn from(owned_fd: OwnedFd) -> Self { + Self(FileDesc::from_inner(owned_fd)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeWriter { + fn from(owned_fd: OwnedFd) -> Self { + Self(FileDesc::from_inner(owned_fd)) + } +} diff --git a/library/std/src/sys/anonymous_pipe/unsupported.rs b/library/std/src/sys/anonymous_pipe/unsupported.rs new file mode 100644 index 0000000000000..dd51e70315e96 --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/unsupported.rs @@ -0,0 +1,23 @@ +use crate::io; +use crate::pipe::{PipeReader, PipeWriter}; +use crate::process::Stdio; +pub use crate::sys::pipe::AnonPipe; + +#[inline] +pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + Err(io::Error::UNSUPPORTED_PLATFORM) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeReader) -> Self { + pipe.0.diverge() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeWriter) -> Self { + pipe.0.diverge() + } +} diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/anonymous_pipe/windows.rs new file mode 100644 index 0000000000000..a48198f8a812b --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/windows.rs @@ -0,0 +1,116 @@ +use crate::os::windows::io::{ + AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, +}; +use crate::pipe::{PipeReader, PipeWriter}; +use crate::process::Stdio; +use crate::sys::c; +use crate::sys::handle::Handle; +use crate::sys_common::{FromInner, IntoInner}; +use crate::{io, ptr}; + +pub type AnonPipe = Handle; + +pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + let mut read_pipe = c::INVALID_HANDLE_VALUE; + let mut write_pipe = c::INVALID_HANDLE_VALUE; + + let ret = unsafe { c::CreatePipe(&mut read_pipe, &mut write_pipe, ptr::null_mut(), 0) }; + + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + unsafe { Ok((Handle::from_raw_handle(read_pipe), Handle::from_raw_handle(write_pipe))) } + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsHandle for PipeReader { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.as_handle() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawHandle for PipeReader { + fn as_raw_handle(&self) -> RawHandle { + self.0.as_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawHandle for PipeReader { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + unsafe { Self(Handle::from_raw_handle(raw_handle)) } + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawHandle for PipeReader { + fn into_raw_handle(self) -> RawHandle { + self.0.into_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedHandle { + fn from(pipe: PipeReader) -> Self { + Handle::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeReader) -> Self { + Self::from(OwnedHandle::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsHandle for PipeWriter { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.as_handle() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawHandle for PipeWriter { + fn as_raw_handle(&self) -> RawHandle { + self.0.as_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawHandle for PipeWriter { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + unsafe { Self(Handle::from_raw_handle(raw_handle)) } + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawHandle for PipeWriter { + fn into_raw_handle(self) -> RawHandle { + self.0.into_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedHandle { + fn from(pipe: PipeWriter) -> Self { + Handle::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeWriter) -> Self { + Self::from(OwnedHandle::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeReader { + fn from(owned_handle: OwnedHandle) -> Self { + Self(Handle::from_inner(owned_handle)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeWriter { + fn from(owned_handle: OwnedHandle) -> Self { + Self(Handle::from_inner(owned_handle)) + } +} diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys/backtrace.rs similarity index 51% rename from library/std/src/sys_common/backtrace.rs rename to library/std/src/sys/backtrace.rs index 67711dbd5bc75..4d939e175cf2e 100644 --- a/library/std/src/sys_common/backtrace.rs +++ b/library/std/src/sys/backtrace.rs @@ -1,50 +1,46 @@ +//! Common code for printing backtraces. +#![forbid(unsafe_op_in_unsafe_fn)] + use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt}; use crate::borrow::Cow; -/// Common code for printing the backtrace in the same way across the different -/// supported platforms. -use crate::env; -use crate::fmt; -use crate::io; use crate::io::prelude::*; use crate::path::{self, Path, PathBuf}; -use crate::sync::{Mutex, PoisonError}; +use crate::sync::{Mutex, MutexGuard, PoisonError}; +use crate::{env, fmt, io}; /// Max number of frames to print. const MAX_NB_FRAMES: usize = 100; -pub fn lock() -> impl Drop { +pub(crate) struct BacktraceLock<'a>(#[allow(dead_code)] MutexGuard<'a, ()>); + +pub(crate) fn lock<'a>() -> BacktraceLock<'a> { static LOCK: Mutex<()> = Mutex::new(()); - LOCK.lock().unwrap_or_else(PoisonError::into_inner) + BacktraceLock(LOCK.lock().unwrap_or_else(PoisonError::into_inner)) } -/// Prints the current backtrace. -pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> { - // There are issues currently linking libbacktrace into tests, and in - // general during std's own unit tests we're not testing this path. In - // test mode immediately return here to optimize away any references to the - // libbacktrace symbols - if cfg!(test) { - return Ok(()); - } - - // Use a lock to prevent mixed output in multithreading context. - // Some platforms also requires it, like `SymFromAddr` on Windows. - unsafe { - let _lock = lock(); - _print(w, format) - } -} +impl BacktraceLock<'_> { + /// Prints the current backtrace. + /// + /// NOTE: this function is not Sync. The caller must hold a mutex lock, or there must be only one thread in the program. + pub(crate) fn print(&mut self, w: &mut dyn Write, format: PrintFmt) -> io::Result<()> { + // There are issues currently linking libbacktrace into tests, and in + // general during std's own unit tests we're not testing this path. In + // test mode immediately return here to optimize away any references to the + // libbacktrace symbols + if cfg!(test) { + return Ok(()); + } -unsafe fn _print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> { - struct DisplayBacktrace { - format: PrintFmt, - } - impl fmt::Display for DisplayBacktrace { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - unsafe { _print_fmt(fmt, self.format) } + struct DisplayBacktrace { + format: PrintFmt, + } + impl fmt::Display for DisplayBacktrace { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + unsafe { _print_fmt(fmt, self.format) } + } } + write!(w, "{}", DisplayBacktrace { format }) } - write!(w, "{}", DisplayBacktrace { format }) } unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result { @@ -65,73 +61,76 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: // Start immediately if we're not using a short backtrace. let mut start = print_fmt != PrintFmt::Short; set_image_base(); - backtrace_rs::trace_unsynchronized(|frame| { - if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES { - return false; - } + // SAFETY: we roll our own locking in this town + unsafe { + backtrace_rs::trace_unsynchronized(|frame| { + if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES { + return false; + } - let mut hit = false; - backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { - hit = true; - - // Any frames between `__rust_begin_short_backtrace` and `__rust_end_short_backtrace` - // are omitted from the backtrace in short mode, `__rust_end_short_backtrace` will be - // called before the panic hook, so we won't ignore any frames if there is no - // invoke of `__rust_begin_short_backtrace`. - if print_fmt == PrintFmt::Short { - if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { - if start && sym.contains("__rust_begin_short_backtrace") { - start = false; - return; - } - if sym.contains("__rust_end_short_backtrace") { - start = true; - return; - } - if !start { - omitted_count += 1; + let mut hit = false; + backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { + hit = true; + + // Any frames between `__rust_begin_short_backtrace` and `__rust_end_short_backtrace` + // are omitted from the backtrace in short mode, `__rust_end_short_backtrace` will be + // called before the panic hook, so we won't ignore any frames if there is no + // invoke of `__rust_begin_short_backtrace`. + if print_fmt == PrintFmt::Short { + if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { + if start && sym.contains("__rust_begin_short_backtrace") { + start = false; + return; + } + if sym.contains("__rust_end_short_backtrace") { + start = true; + return; + } + if !start { + omitted_count += 1; + } } } - } - if start { - if omitted_count > 0 { - debug_assert!(print_fmt == PrintFmt::Short); - // only print the message between the middle of frames - if !first_omit { - let _ = writeln!( - bt_fmt.formatter(), - " [... omitted {} frame{} ...]", - omitted_count, - if omitted_count > 1 { "s" } else { "" } - ); + if start { + if omitted_count > 0 { + debug_assert!(print_fmt == PrintFmt::Short); + // only print the message between the middle of frames + if !first_omit { + let _ = writeln!( + bt_fmt.formatter(), + " [... omitted {} frame{} ...]", + omitted_count, + if omitted_count > 1 { "s" } else { "" } + ); + } + first_omit = false; + omitted_count = 0; } - first_omit = false; - omitted_count = 0; + res = bt_fmt.frame().symbol(frame, symbol); + } + }); + #[cfg(target_os = "nto")] + if libc::__my_thread_exit as *mut libc::c_void == frame.ip() { + if !hit && start { + use crate::backtrace_rs::SymbolName; + res = bt_fmt.frame().print_raw( + frame.ip(), + Some(SymbolName::new("__my_thread_exit".as_bytes())), + None, + None, + ); } - res = bt_fmt.frame().symbol(frame, symbol); + return false; } - }); - #[cfg(target_os = "nto")] - if libc::__my_thread_exit as *mut libc::c_void == frame.ip() { if !hit && start { - use crate::backtrace_rs::SymbolName; - res = bt_fmt.frame().print_raw( - frame.ip(), - Some(SymbolName::new("__my_thread_exit".as_bytes())), - None, - None, - ); + res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); } - return false; - } - if !hit && start { - res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); - } - idx += 1; - res.is_ok() - }); + idx += 1; + res.is_ok() + }) + }; res?; bt_fmt.finish()?; if print_fmt == PrintFmt::Short { diff --git a/library/std/src/sys/exit_guard.rs b/library/std/src/sys/exit_guard.rs new file mode 100644 index 0000000000000..5a090f506661d --- /dev/null +++ b/library/std/src/sys/exit_guard.rs @@ -0,0 +1,72 @@ +cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + /// pthread_t is a pointer on some platforms, + /// so we wrap it in this to impl Send + Sync. + #[derive(Clone, Copy)] + #[repr(transparent)] + struct PThread(libc::pthread_t); + // Safety: pthread_t is safe to send between threads + unsafe impl Send for PThread {} + // Safety: pthread_t is safe to share between threads + unsafe impl Sync for PThread {} + /// Mitigation for + /// + /// On glibc, `libc::exit` has been observed to not always be thread-safe. + /// It is currently unclear whether that is a glibc bug or allowed by the standard. + /// To mitigate this problem, we ensure that only one + /// Rust thread calls `libc::exit` (or returns from `main`) by calling this function before + /// calling `libc::exit` (or returning from `main`). + /// + /// Technically, this is not enough to ensure soundness, since other code directly calling + /// `libc::exit` will still race with this. + /// + /// *This function does not itself call `libc::exit`.* This is so it can also be used + /// to guard returning from `main`. + /// + /// This function will return only the first time it is called in a process. + /// + /// * If it is called again on the same thread as the first call, it will abort. + /// * If it is called again on a different thread, it will wait in a loop + /// (waiting for the process to exit). + #[cfg_attr(any(test, doctest), allow(dead_code))] + pub(crate) fn unique_thread_exit() { + let this_thread_id = unsafe { libc::pthread_self() }; + use crate::sync::{Mutex, PoisonError}; + static EXITING_THREAD_ID: Mutex> = Mutex::new(None); + let mut exiting_thread_id = + EXITING_THREAD_ID.lock().unwrap_or_else(PoisonError::into_inner); + match *exiting_thread_id { + None => { + // This is the first thread to call `unique_thread_exit`, + // and this is the first time it is called. + // Set EXITING_THREAD_ID to this thread's ID and return. + *exiting_thread_id = Some(PThread(this_thread_id)); + }, + Some(exiting_thread_id) if exiting_thread_id.0 == this_thread_id => { + // This is the first thread to call `unique_thread_exit`, + // but this is the second time it is called. + // Abort the process. + core::panicking::panic_nounwind("std::process::exit called re-entrantly") + } + Some(_) => { + // This is not the first thread to call `unique_thread_exit`. + // Pause until the process exits. + drop(exiting_thread_id); + loop { + // Safety: libc::pause is safe to call. + unsafe { libc::pause(); } + } + } + } + } + } else { + /// Mitigation for + /// + /// Mitigation is ***NOT*** implemented on this platform, either because this platform + /// is not affected, or because mitigation is not yet implemented for this platform. + #[cfg_attr(any(test, doctest), allow(dead_code))] + pub(crate) fn unique_thread_exit() { + // Mitigation not required on platforms where `exit` is thread-safe. + } + } +} diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 8f70cefc60121..a86b3628f249a 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -1,3 +1,5 @@ +#![allow(unsafe_op_in_unsafe_fn)] + /// The PAL (platform abstraction layer) contains platform-specific abstractions /// for implementing the features in the other submodules, e.g. UNIX file /// descriptors. @@ -5,7 +7,10 @@ mod pal; mod personality; +pub mod anonymous_pipe; +pub mod backtrace; pub mod cmath; +pub mod exit_guard; pub mod os_str; pub mod path; pub mod sync; diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index 18b969bca85a6..0f8bd6453528e 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -3,13 +3,11 @@ use crate::borrow::Cow; use crate::collections::TryReserveError; -use crate::fmt; use crate::fmt::Write; -use crate::mem; use crate::rc::Rc; -use crate::str; use crate::sync::Arc; use crate::sys_common::{AsInner, IntoInner}; +use crate::{fmt, mem, str}; #[cfg(test)] mod tests; @@ -176,6 +174,11 @@ impl Buf { self.inner.extend_from_slice(&s.inner) } + #[inline] + pub fn leak<'a>(self) -> &'a mut Slice { + unsafe { mem::transmute(self.inner.leak()) } + } + #[inline] pub fn into_box(self) -> Box { unsafe { mem::transmute(self.inner.into_boxed_slice()) } @@ -197,10 +200,20 @@ impl Buf { self.as_slice().into_rc() } - /// Part of a hack to make PathBuf::push/pop more efficient. + /// Provides plumbing to core `Vec::truncate`. + /// More well behaving alternative to allowing outer types + /// full mutable access to the core `Vec`. + #[inline] + pub(crate) fn truncate(&mut self, len: usize) { + self.inner.truncate(len); + } + + /// Provides plumbing to core `Vec::extend_from_slice`. + /// More well behaving alternative to allowing outer types + /// full mutable access to the core `Vec`. #[inline] - pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec { - &mut self.inner + pub(crate) fn extend_from_slice(&mut self, other: &[u8]) { + self.inner.extend_from_slice(other); } } diff --git a/library/std/src/sys/os_str/mod.rs b/library/std/src/sys/os_str/mod.rs index b509729475bf7..345e661586d03 100644 --- a/library/std/src/sys/os_str/mod.rs +++ b/library/std/src/sys/os_str/mod.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + cfg_if::cfg_if! { if #[cfg(any( target_os = "windows", diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index b3ceb55802dc5..ed975ba58b5e2 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -1,13 +1,12 @@ -/// The underlying OsString/OsStr implementation on Windows is a -/// wrapper around the "WTF-8" encoding; see the `wtf8` module for more. +//! The underlying OsString/OsStr implementation on Windows is a +//! wrapper around the "WTF-8" encoding; see the `wtf8` module for more. use crate::borrow::Cow; use crate::collections::TryReserveError; -use crate::fmt; -use crate::mem; use crate::rc::Rc; use crate::sync::Arc; use crate::sys_common::wtf8::{check_utf8_boundary, Wtf8, Wtf8Buf}; use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::{fmt, mem}; #[derive(Clone, Hash)] pub struct Buf { @@ -70,7 +69,7 @@ impl Buf { #[inline] pub unsafe fn from_encoded_bytes_unchecked(s: Vec) -> Self { - Self { inner: Wtf8Buf::from_bytes_unchecked(s) } + unsafe { Self { inner: Wtf8Buf::from_bytes_unchecked(s) } } } pub fn with_capacity(capacity: usize) -> Buf { @@ -138,6 +137,11 @@ impl Buf { self.inner.shrink_to(min_capacity) } + #[inline] + pub fn leak<'a>(self) -> &'a mut Slice { + unsafe { mem::transmute(self.inner.leak()) } + } + #[inline] pub fn into_box(self) -> Box { unsafe { mem::transmute(self.inner.into_box()) } @@ -159,10 +163,20 @@ impl Buf { self.as_slice().into_rc() } - /// Part of a hack to make PathBuf::push/pop more efficient. + /// Provides plumbing to core `Vec::truncate`. + /// More well behaving alternative to allowing outer types + /// full mutable access to the core `Vec`. + #[inline] + pub(crate) fn truncate(&mut self, len: usize) { + self.inner.truncate(len); + } + + /// Provides plumbing to core `Vec::extend_from_slice`. + /// More well behaving alternative to allowing outer types + /// full mutable access to the core `Vec`. #[inline] - pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec { - self.inner.as_mut_vec_for_path_buf() + pub(crate) fn extend_from_slice(&mut self, other: &[u8]) { + self.inner.extend_from_slice(other); } } @@ -174,7 +188,7 @@ impl Slice { #[inline] pub unsafe fn from_encoded_bytes_unchecked(s: &[u8]) -> &Slice { - mem::transmute(Wtf8::from_bytes_unchecked(s)) + unsafe { mem::transmute(Wtf8::from_bytes_unchecked(s)) } } #[track_caller] diff --git a/library/std/src/sys/pal/common/alloc.rs b/library/std/src/sys/pal/common/alloc.rs index 598b6db71f5de..1b465f95d1bc3 100644 --- a/library/std/src/sys/pal/common/alloc.rs +++ b/library/std/src/sys/pal/common/alloc.rs @@ -1,6 +1,6 @@ +#![forbid(unsafe_op_in_unsafe_fn)] use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::cmp; -use crate::ptr; +use crate::{cmp, ptr}; // The minimum alignment guaranteed by the architecture. This value is used to // add fast paths for low alignment values. @@ -46,14 +46,16 @@ pub unsafe fn realloc_fallback( old_layout: Layout, new_size: usize, ) -> *mut u8 { - // Docs for GlobalAlloc::realloc require this to be valid: - let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); + // SAFETY: Docs for GlobalAlloc::realloc require this to be valid + unsafe { + let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); - let new_ptr = GlobalAlloc::alloc(alloc, new_layout); - if !new_ptr.is_null() { - let size = cmp::min(old_layout.size(), new_size); - ptr::copy_nonoverlapping(ptr, new_ptr, size); - GlobalAlloc::dealloc(alloc, ptr, old_layout); + let new_ptr = GlobalAlloc::alloc(alloc, new_layout); + if !new_ptr.is_null() { + let size = cmp::min(old_layout.size(), new_size); + ptr::copy_nonoverlapping(ptr, new_ptr, size); + GlobalAlloc::dealloc(alloc, ptr, old_layout); + } + new_ptr } - new_ptr } diff --git a/library/std/src/sys/pal/common/small_c_string.rs b/library/std/src/sys/pal/common/small_c_string.rs index 37812fc0659a2..3c96714b5c58c 100644 --- a/library/std/src/sys/pal/common/small_c_string.rs +++ b/library/std/src/sys/pal/common/small_c_string.rs @@ -1,8 +1,7 @@ use crate::ffi::{CStr, CString}; use crate::mem::MaybeUninit; use crate::path::Path; -use crate::slice; -use crate::{io, ptr}; +use crate::{io, ptr, slice}; // Make sure to stay under 4096 so the compiler doesn't insert a probe frame: // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html diff --git a/library/std/src/sys/pal/common/tests.rs b/library/std/src/sys/pal/common/tests.rs index e72d02203da10..b7698907070c7 100644 --- a/library/std/src/sys/pal/common/tests.rs +++ b/library/std/src/sys/pal/common/tests.rs @@ -1,8 +1,9 @@ +use core::iter::repeat; + use crate::ffi::CString; use crate::hint::black_box; use crate::path::Path; use crate::sys::common::small_c_string::run_path_with_cstr; -use core::iter::repeat; #[test] fn stack_allocation_works() { diff --git a/library/std/src/sys/pal/hermit/alloc.rs b/library/std/src/sys/pal/hermit/alloc.rs index 2cd0db909403b..f10d5f9227e63 100644 --- a/library/std/src/sys/pal/hermit/alloc.rs +++ b/library/std/src/sys/pal/hermit/alloc.rs @@ -1,31 +1,28 @@ use super::hermit_abi; use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::ptr; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - hermit_abi::malloc(layout.size(), layout.align()) - } - - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - let addr = hermit_abi::malloc(layout.size(), layout.align()); - - if !addr.is_null() { - ptr::write_bytes(addr, 0x00, layout.size()); - } - - addr + let size = layout.size(); + let align = layout.align(); + unsafe { hermit_abi::malloc(size, align) } } #[inline] unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - hermit_abi::free(ptr, layout.size(), layout.align()) + let size = layout.size(); + let align = layout.align(); + unsafe { + hermit_abi::free(ptr, size, align); + } } #[inline] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - hermit_abi::realloc(ptr, layout.size(), layout.align(), new_size) + let size = layout.size(); + let align = layout.align(); + unsafe { hermit_abi::realloc(ptr, size, align, new_size) } } } diff --git a/library/std/src/sys/pal/hermit/args.rs b/library/std/src/sys/pal/hermit/args.rs index 220a76e4b1237..51afe3434aedc 100644 --- a/library/std/src/sys/pal/hermit/args.rs +++ b/library/std/src/sys/pal/hermit/args.rs @@ -1,12 +1,8 @@ use crate::ffi::{c_char, CStr, OsString}; -use crate::fmt; use crate::os::hermit::ffi::OsStringExt; -use crate::ptr; -use crate::sync::atomic::{ - AtomicIsize, AtomicPtr, - Ordering::{Acquire, Relaxed, Release}, -}; -use crate::vec; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sync::atomic::{AtomicIsize, AtomicPtr}; +use crate::{fmt, ptr, vec}; static ARGC: AtomicIsize = AtomicIsize::new(0); static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut()); diff --git a/library/std/src/sys/pal/hermit/fd.rs b/library/std/src/sys/pal/hermit/fd.rs index d7dab08cfbd57..79fc13bd4a87f 100644 --- a/library/std/src/sys/pal/hermit/fd.rs +++ b/library/std/src/sys/pal/hermit/fd.rs @@ -1,13 +1,15 @@ #![unstable(reason = "not public", issue = "none", feature = "fd")] use super::hermit_abi; -use crate::io::{self, Read}; -use crate::os::hermit::io::{FromRawFd, OwnedFd, RawFd}; -use crate::sys::cvt; -use crate::sys::unsupported; +use crate::cmp; +use crate::io::{self, IoSlice, IoSliceMut, Read}; +use crate::os::hermit::io::{FromRawFd, OwnedFd, RawFd, *}; +use crate::sys::{cvt, unsupported}; use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::os::hermit::io::*; +const fn max_iov() -> usize { + hermit_abi::IOV_MAX +} #[derive(Debug)] pub struct FileDesc { @@ -21,6 +23,22 @@ impl FileDesc { Ok(result as usize) } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let ret = cvt(unsafe { + hermit_abi::readv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut hermit_abi::iovec as *const hermit_abi::iovec, + cmp::min(bufs.len(), max_iov()), + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { let mut me = self; (&mut me).read_to_end(buf) @@ -32,6 +50,22 @@ impl FileDesc { Ok(result as usize) } + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let ret = cvt(unsafe { + hermit_abi::writev( + self.as_raw_fd(), + bufs.as_ptr() as *const hermit_abi::iovec, + cmp::min(bufs.len(), max_iov()), + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn duplicate(&self) -> io::Result { self.duplicate_path(&[]) } @@ -77,7 +111,8 @@ impl FromInner for FileDesc { impl FromRawFd for FileDesc { unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self { fd: FromRawFd::from_raw_fd(raw_fd) } + let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) }; + Self { fd } } } diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index a4a16e6e86b0c..aaf1a044d0613 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -1,24 +1,20 @@ use super::fd::FileDesc; use super::hermit_abi::{ self, dirent64, stat as stat_struct, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, - O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, + O_DIRECTORY, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, }; use crate::ffi::{CStr, OsStr, OsString}; -use crate::fmt; -use crate::io::{self, Error, ErrorKind}; -use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem; +use crate::io::{self, BorrowedCursor, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; use crate::os::hermit::ffi::OsStringExt; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; use crate::sync::Arc; use crate::sys::common::small_c_string::run_path_with_cstr; -use crate::sys::cvt; use crate::sys::time::SystemTime; -use crate::sys::unsupported; +use crate::sys::{cvt, unsupported}; +pub use crate::sys_common::fs::{copy, exists}; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; - -pub use crate::sys_common::fs::{copy, try_exists}; +use crate::{fmt, mem}; #[derive(Debug)] pub struct File(FileDesc); @@ -62,7 +58,7 @@ pub struct DirEntry { /// 64-bit inode number ino: u64, /// File type - type_: u32, + type_: u8, /// name of the entry name: OsString, } @@ -90,7 +86,7 @@ pub struct FilePermissions { #[derive(Copy, Clone, Eq, Debug)] pub struct FileType { - mode: u32, + mode: u8, } impl PartialEq for FileType { @@ -112,31 +108,23 @@ pub struct DirBuilder { impl FileAttr { pub fn modified(&self) -> io::Result { - Ok(SystemTime::new( - self.stat_val.st_mtime.try_into().unwrap(), - self.stat_val.st_mtime_nsec.try_into().unwrap(), - )) + Ok(SystemTime::new(self.stat_val.st_mtim.tv_sec, self.stat_val.st_mtim.tv_nsec)) } pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new( - self.stat_val.st_atime.try_into().unwrap(), - self.stat_val.st_atime_nsec.try_into().unwrap(), - )) + Ok(SystemTime::new(self.stat_val.st_atim.tv_sec, self.stat_val.st_atim.tv_nsec)) } pub fn created(&self) -> io::Result { - Ok(SystemTime::new( - self.stat_val.st_ctime.try_into().unwrap(), - self.stat_val.st_ctime_nsec.try_into().unwrap(), - )) + Ok(SystemTime::new(self.stat_val.st_ctim.tv_sec, self.stat_val.st_ctim.tv_nsec)) } pub fn size(&self) -> u64 { self.stat_val.st_size as u64 } + pub fn perm(&self) -> FilePermissions { - FilePermissions { mode: (self.stat_val.st_mode) } + FilePermissions { mode: self.stat_val.st_mode } } pub fn file_type(&self) -> FileType { @@ -220,7 +208,7 @@ impl Iterator for ReadDir { let entry = DirEntry { root: self.inner.root.clone(), ino: dir.d_ino, - type_: dir.d_type as u32, + type_: dir.d_type, name: OsString::from_vec(name_bytes.to_vec()), }; @@ -251,7 +239,7 @@ impl DirEntry { } pub fn file_type(&self) -> io::Result { - Ok(FileType { mode: self.type_ as u32 }) + Ok(FileType { mode: self.type_ }) } #[allow(dead_code)] @@ -385,12 +373,12 @@ impl File { } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - crate::io::default_read_vectored(|buf| self.read(buf), bufs) + self.0.read_vectored(bufs) } #[inline] pub fn is_read_vectored(&self) -> bool { - false + self.0.is_read_vectored() } pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { @@ -402,12 +390,12 @@ impl File { } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - crate::io::default_write_vectored(|buf| self.write(buf), bufs) + self.0.write_vectored(bufs) } #[inline] pub fn is_write_vectored(&self) -> bool { - false + self.0.is_write_vectored() } #[inline] @@ -439,13 +427,13 @@ impl DirBuilder { pub fn mkdir(&self, path: &Path) -> io::Result<()> { run_path_with_cstr(path, &|path| { - cvt(unsafe { hermit_abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ()) + cvt(unsafe { hermit_abi::mkdir(path.as_ptr(), self.mode.into()) }).map(|_| ()) }) } #[allow(dead_code)] pub fn set_mode(&mut self, mode: u32) { - self.mode = mode as u32; + self.mode = mode; } } @@ -496,13 +484,15 @@ impl IntoRawFd for File { impl FromRawFd for File { unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) + let file_desc = unsafe { FileDesc::from_raw_fd(raw_fd) }; + Self(file_desc) } } pub fn readdir(path: &Path) -> io::Result { - let fd_raw = - run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::opendir(path.as_ptr()) }))?; + let fd_raw = run_path_with_cstr(path, &|path| { + cvt(unsafe { hermit_abi::open(path.as_ptr(), O_RDONLY | O_DIRECTORY, 0) }) + })?; let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; let root = path.to_path_buf(); diff --git a/library/std/src/sys/pal/hermit/futex.rs b/library/std/src/sys/pal/hermit/futex.rs index 571b288565871..21c5facd52fbd 100644 --- a/library/std/src/sys/pal/hermit/futex.rs +++ b/library/std/src/sys/pal/hermit/futex.rs @@ -3,6 +3,11 @@ use crate::ptr::null; use crate::sync::atomic::AtomicU32; use crate::time::Duration; +/// An atomic for use as a futex that is at least 8-bits but may be larger. +pub type SmallAtomic = AtomicU32; +/// Must be the underlying type of SmallAtomic +pub type SmallPrimitive = u32; + pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { // Calculate the timeout as a relative timespec. // @@ -10,7 +15,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - let timespec = timeout.and_then(|dur| { Some(hermit_abi::timespec { tv_sec: dur.as_secs().try_into().ok()?, - tv_nsec: dur.subsec_nanos().into(), + tv_nsec: dur.subsec_nanos().try_into().ok()?, }) }); diff --git a/library/std/src/sys/pal/hermit/io.rs b/library/std/src/sys/pal/hermit/io.rs new file mode 100644 index 0000000000000..aad1eef71e9b0 --- /dev/null +++ b/library/std/src/sys/pal/hermit/io.rs @@ -0,0 +1,82 @@ +use hermit_abi::{c_void, iovec}; + +use crate::marker::PhantomData; +use crate::os::hermit::io::{AsFd, AsRawFd}; +use crate::slice; + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct IoSlice<'a> { + vec: iovec, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice { + vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } +} + +#[repr(transparent)] +pub struct IoSliceMut<'a> { + vec: iovec, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut { + vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSliceMut beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } +} + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + hermit_abi::isatty(fd.as_raw_fd()) +} diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index a64323a3a296e..ef406b9ec7f0d 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -13,7 +13,8 @@ //! compiling for wasm. That way it's a compile time error for something that's //! guaranteed to be a runtime error! -#![allow(missing_docs, nonstandard_style, unsafe_op_in_unsafe_fn)] +#![deny(unsafe_op_in_unsafe_fn)] +#![allow(missing_docs, nonstandard_style)] use crate::os::raw::c_char; @@ -23,7 +24,6 @@ pub mod env; pub mod fd; pub mod fs; pub mod futex; -#[path = "../unsupported/io.rs"] pub mod io; pub mod net; pub mod os; @@ -33,9 +33,6 @@ pub mod pipe; pub mod process; pub mod stdio; pub mod thread; -pub mod thread_local_dtor; -#[path = "../unsupported/thread_local_key.rs"] -pub mod thread_local_key; pub mod time; use crate::io::ErrorKind; @@ -53,9 +50,7 @@ pub fn unsupported_err() -> crate::io::Error { } pub fn abort_internal() -> ! { - unsafe { - hermit_abi::abort(); - } + unsafe { hermit_abi::abort() } } pub fn hashmap_random_keys() -> (u64, u64) { @@ -84,7 +79,9 @@ pub extern "C" fn __rust_abort() { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { - args::init(argc, argv); + unsafe { + args::init(argc, argv); + } } // SAFETY: must be called only once during runtime cleanup. @@ -98,7 +95,6 @@ pub unsafe extern "C" fn runtime_entry( argv: *const *const c_char, env: *const *const c_char, ) -> ! { - use thread_local_dtor::run_dtors; extern "C" { fn main(argc: isize, argv: *const *const c_char) -> i32; } @@ -106,10 +102,12 @@ pub unsafe extern "C" fn runtime_entry( // initialize environment os::init_environment(env as *const *const i8); - let result = main(argc as isize, argv); + let result = unsafe { main(argc as isize, argv) }; - run_dtors(); - hermit_abi::exit(result); + unsafe { + crate::sys::thread_local::destructors::run(); + } + unsafe { hermit_abi::exit(result) } } #[inline] diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs index 00dbca86a4bae..416469c003738 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/pal/hermit/net.rs @@ -1,17 +1,16 @@ #![allow(dead_code)] +use core::ffi::c_int; + use super::fd::FileDesc; -use crate::cmp; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::mem; use crate::net::{Shutdown, SocketAddr}; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; use crate::sys::time::Instant; use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; - -use core::ffi::c_int; +use crate::{cmp, mem}; #[allow(unused_extern_crates)] pub extern crate hermit_abi as netc; @@ -175,12 +174,12 @@ impl Socket { } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - crate::io::default_read_vectored(|b| self.read(b), bufs) + self.0.read_vectored(bufs) } #[inline] pub fn is_read_vectored(&self) -> bool { - false + self.0.is_read_vectored() } fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> { @@ -209,16 +208,15 @@ impl Socket { } pub fn write(&self, buf: &[u8]) -> io::Result { - let sz = cvt(unsafe { netc::write(self.0.as_raw_fd(), buf.as_ptr(), buf.len()) })?; - Ok(sz.try_into().unwrap()) + self.0.write(buf) } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - crate::io::default_write_vectored(|b| self.write(b), bufs) + self.0.write_vectored(bufs) } pub fn is_write_vectored(&self) -> bool { - false + self.0.is_write_vectored() } pub fn set_timeout(&self, dur: Option, kind: i32) -> io::Result<()> { @@ -265,7 +263,7 @@ impl Socket { Shutdown::Read => netc::SHUT_RD, Shutdown::Both => netc::SHUT_RDWR, }; - cvt(unsafe { netc::shutdown_socket(self.as_raw_fd(), how) })?; + cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; Ok(()) } diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index cc6781238319b..f8ea80afa43f1 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -1,17 +1,15 @@ +use core::slice::memchr; + use super::hermit_abi; use crate::collections::HashMap; use crate::error::Error as StdError; use crate::ffi::{CStr, OsStr, OsString}; -use crate::fmt; -use crate::io; use crate::marker::PhantomData; use crate::os::hermit::ffi::OsStringExt; use crate::path::{self, PathBuf}; -use crate::str; use crate::sync::Mutex; use crate::sys::unsupported; -use crate::vec; -use core::slice::memchr; +use crate::{fmt, io, str, vec}; pub fn errno() -> i32 { unsafe { hermit_abi::get_errno() } @@ -70,21 +68,21 @@ pub fn current_exe() -> io::Result { unsupported() } -static mut ENV: Option>> = None; +static ENV: Mutex>> = Mutex::new(None); pub fn init_environment(env: *const *const i8) { - unsafe { - ENV = Some(Mutex::new(HashMap::new())); + let mut guard = ENV.lock().unwrap(); + let map = guard.insert(HashMap::new()); - if env.is_null() { - return; - } + if env.is_null() { + return; + } - let mut guard = ENV.as_ref().unwrap().lock().unwrap(); + unsafe { let mut environ = env; while !(*environ).is_null() { if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) { - guard.insert(key, value); + map.insert(key, value); } environ = environ.add(1); } @@ -156,34 +154,26 @@ impl Iterator for Env { /// Returns a vector of (variable, value) byte-vector pairs for all the /// environment variables of the current process. pub fn env() -> Env { - unsafe { - let guard = ENV.as_ref().unwrap().lock().unwrap(); - let mut result = Vec::new(); + let guard = ENV.lock().unwrap(); + let env = guard.as_ref().unwrap(); - for (key, value) in guard.iter() { - result.push((key.clone(), value.clone())); - } + let result = env.iter().map(|(key, value)| (key.clone(), value.clone())).collect::>(); - return Env { iter: result.into_iter() }; - } + Env { iter: result.into_iter() } } pub fn getenv(k: &OsStr) -> Option { - unsafe { ENV.as_ref().unwrap().lock().unwrap().get_mut(k).cloned() } + ENV.lock().unwrap().as_ref().unwrap().get(k).cloned() } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - unsafe { - let (k, v) = (k.to_owned(), v.to_owned()); - ENV.as_ref().unwrap().lock().unwrap().insert(k, v); - } +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + ENV.lock().unwrap().as_mut().unwrap().insert(k, v); Ok(()) } -pub fn unsetenv(k: &OsStr) -> io::Result<()> { - unsafe { - ENV.as_ref().unwrap().lock().unwrap().remove(k); - } +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + ENV.lock().unwrap().as_mut().unwrap().remove(k); Ok(()) } @@ -196,11 +186,9 @@ pub fn home_dir() -> Option { } pub fn exit(code: i32) -> ! { - unsafe { - hermit_abi::exit(code); - } + unsafe { hermit_abi::exit(code) } } pub fn getpid() -> u32 { - unsafe { hermit_abi::getpid() } + unsafe { hermit_abi::getpid() as u32 } } diff --git a/library/std/src/sys/pal/hermit/stdio.rs b/library/std/src/sys/pal/hermit/stdio.rs index 777c57b391c89..3ea00f5cc5ec9 100644 --- a/library/std/src/sys/pal/hermit/stdio.rs +++ b/library/std/src/sys/pal/hermit/stdio.rs @@ -1,6 +1,9 @@ use super::hermit_abi; use crate::io; use crate::io::{IoSlice, IoSliceMut}; +use crate::mem::ManuallyDrop; +use crate::os::hermit::io::FromRawFd; +use crate::sys::fd::FileDesc; pub struct Stdin; pub struct Stdout; @@ -13,12 +16,14 @@ impl Stdin { } impl io::Read for Stdin { - fn read(&mut self, data: &mut [u8]) -> io::Result { - self.read_vectored(&mut [IoSliceMut::new(data)]) + fn read(&mut self, buf: &mut [u8]) -> io::Result { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDIN_FILENO)).read(buf) } } - fn read_vectored(&mut self, _data: &mut [IoSliceMut<'_>]) -> io::Result { - Ok(0) + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDIN_FILENO)).read_vectored(bufs) + } } #[inline] @@ -34,27 +39,13 @@ impl Stdout { } impl io::Write for Stdout { - fn write(&mut self, data: &[u8]) -> io::Result { - let len; - - unsafe { len = hermit_abi::write(1, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print")) - } else { - Ok(len as usize) - } + fn write(&mut self, buf: &[u8]) -> io::Result { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDOUT_FILENO)).write(buf) } } - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - let len; - - unsafe { len = hermit_abi::write(1, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print")) - } else { - Ok(len as usize) + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDOUT_FILENO)).write_vectored(bufs) } } @@ -75,27 +66,13 @@ impl Stderr { } impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - let len; - - unsafe { len = hermit_abi::write(2, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print")) - } else { - Ok(len as usize) - } + fn write(&mut self, buf: &[u8]) -> io::Result { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDERR_FILENO)).write(buf) } } - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - let len; - - unsafe { len = hermit_abi::write(2, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print")) - } else { - Ok(len as usize) + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDERR_FILENO)).write_vectored(bufs) } } @@ -109,10 +86,10 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = 0; +pub const STDIN_BUF_SIZE: usize = 128; -pub fn is_ebadf(_err: &io::Error) -> bool { - true +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(hermit_abi::EBADF) } pub fn panic_output() -> Option { diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index b336dcd6860e4..6321f92e3d9d0 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -1,13 +1,11 @@ #![allow(dead_code)] use super::hermit_abi; -use super::thread_local_dtor::run_dtors; use crate::ffi::CStr; -use crate::io; -use crate::mem; +use crate::mem::ManuallyDrop; use crate::num::NonZero; -use crate::ptr; use crate::time::Duration; +use crate::{io, ptr}; pub type Tid = hermit_abi::Tid; @@ -27,18 +25,22 @@ impl Thread { core_id: isize, ) -> io::Result { let p = Box::into_raw(Box::new(p)); - let tid = hermit_abi::spawn2( - thread_start, - p.expose_provenance(), - hermit_abi::Priority::into(hermit_abi::NORMAL_PRIO), - stack, - core_id, - ); + let tid = unsafe { + hermit_abi::spawn2( + thread_start, + p.expose_provenance(), + hermit_abi::Priority::into(hermit_abi::NORMAL_PRIO), + stack, + core_id, + ) + }; return if tid == 0 { // The thread failed to start and as a result p was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. - drop(Box::from_raw(p)); + unsafe { + drop(Box::from_raw(p)); + } Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Unable to create thread!")) } else { Ok(Thread { tid: tid }) @@ -50,13 +52,15 @@ impl Thread { Box::from_raw(ptr::with_exposed_provenance::>(main).cast_mut())(); // run all destructors - run_dtors(); + crate::sys::thread_local::destructors::run(); } } } pub unsafe fn new(stack: usize, p: Box) -> io::Result { - Thread::new_with_coreid(stack, p, -1 /* = no specific core */) + unsafe { + Thread::new_with_coreid(stack, p, -1 /* = no specific core */) + } } #[inline] @@ -91,12 +95,10 @@ impl Thread { #[inline] pub fn into_id(self) -> Tid { - let id = self.tid; - mem::forget(self); - id + ManuallyDrop::new(self).tid } } pub fn available_parallelism() -> io::Result> { - unsafe { Ok(NonZero::new_unchecked(hermit_abi::get_processor_count())) } + unsafe { Ok(NonZero::new_unchecked(hermit_abi::available_parallelism())) } } diff --git a/library/std/src/sys/pal/hermit/thread_local_dtor.rs b/library/std/src/sys/pal/hermit/thread_local_dtor.rs deleted file mode 100644 index 98adaf4bff1aa..0000000000000 --- a/library/std/src/sys/pal/hermit/thread_local_dtor.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![cfg(target_thread_local)] -#![unstable(feature = "thread_local_internals", issue = "none")] - -// Simplify dtor registration by using a list of destructors. -// The this solution works like the implementation of macOS and -// doesn't additional OS support - -use crate::cell::RefCell; - -#[thread_local] -static DTORS: RefCell> = RefCell::new(Vec::new()); - -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - match DTORS.try_borrow_mut() { - Ok(mut dtors) => dtors.push((t, dtor)), - Err(_) => rtabort!("global allocator may not use TLS"), - } -} - -// every thread call this function to run through all possible destructors -pub unsafe fn run_dtors() { - let mut list = DTORS.take(); - while !list.is_empty() { - for (ptr, dtor) in list { - dtor(ptr); - } - list = DTORS.take(); - } -} diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index 2bf24462fa825..2c87c4860a27a 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -1,10 +1,13 @@ #![allow(dead_code)] -use super::hermit_abi::{self, timespec, CLOCK_MONOTONIC, CLOCK_REALTIME, NSEC_PER_SEC}; +use core::hash::{Hash, Hasher}; + +use super::hermit_abi::{self, timespec, CLOCK_MONOTONIC, CLOCK_REALTIME}; use crate::cmp::Ordering; use crate::ops::{Add, AddAssign, Sub, SubAssign}; use crate::time::Duration; -use core::hash::{Hash, Hasher}; + +const NSEC_PER_SEC: i32 = 1_000_000_000; #[derive(Copy, Clone, Debug)] struct Timespec { @@ -16,8 +19,8 @@ impl Timespec { Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } } - const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { - assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); + const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec { + assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC); // SAFETY: The assert above checks tv_nsec is within the valid range Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } } } @@ -32,7 +35,7 @@ impl Timespec { } else { Duration::new( (self.t.tv_sec - 1 - other.t.tv_sec) as u64, - self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.t.tv_nsec as u32, + (self.t.tv_nsec + NSEC_PER_SEC - other.t.tv_nsec) as u32, ) }) } else { @@ -48,9 +51,9 @@ impl Timespec { // Nano calculations can't overflow because nanos are <1B which fit // in a u32. - let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; - if nsec >= NSEC_PER_SEC as u32 { - nsec -= NSEC_PER_SEC as u32; + let mut nsec = other.subsec_nanos() + u32::try_from(self.t.tv_nsec).unwrap(); + if nsec >= NSEC_PER_SEC.try_into().unwrap() { + nsec -= u32::try_from(NSEC_PER_SEC).unwrap(); secs = secs.checked_add(1)?; } Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } }) @@ -200,7 +203,7 @@ pub struct SystemTime(Timespec); pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); impl SystemTime { - pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime { SystemTime(Timespec::new(tv_sec, tv_nsec)) } diff --git a/library/std/src/sys/pal/itron/error.rs b/library/std/src/sys/pal/itron/error.rs index fbc822d4eb6e1..8ff3017c61471 100644 --- a/library/std/src/sys/pal/itron/error.rs +++ b/library/std/src/sys/pal/itron/error.rs @@ -1,6 +1,6 @@ -use crate::{fmt, io::ErrorKind}; - use super::abi; +use crate::fmt; +use crate::io::ErrorKind; /// Wraps a μITRON error code. #[derive(Debug, Copy, Clone)] @@ -9,7 +9,7 @@ pub struct ItronError { } impl ItronError { - /// Construct `ItronError` from the specified error code. Returns `None` if the + /// Constructs `ItronError` from the specified error code. Returns `None` if the /// error code does not represent a failure or warning. #[inline] pub fn new(er: abi::ER) -> Option { @@ -22,7 +22,7 @@ impl ItronError { if let Some(error) = Self::new(er) { Err(error) } else { Ok(er) } } - /// Get the raw error code. + /// Gets the raw error code. #[inline] pub fn as_raw(&self) -> abi::ER { self.er diff --git a/library/std/src/sys/pal/itron/spin.rs b/library/std/src/sys/pal/itron/spin.rs index 44d409444bca4..6a9a7c72deb7d 100644 --- a/library/std/src/sys/pal/itron/spin.rs +++ b/library/std/src/sys/pal/itron/spin.rs @@ -1,9 +1,7 @@ use super::abi; -use crate::{ - cell::UnsafeCell, - mem::MaybeUninit, - sync::atomic::{AtomicBool, AtomicUsize, Ordering}, -}; +use crate::cell::UnsafeCell; +use crate::mem::MaybeUninit; +use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; /// A mutex implemented by `dis_dsp` (for intra-core synchronization) and a /// spinlock (for inter-core synchronization). diff --git a/library/std/src/sys/pal/itron/task.rs b/library/std/src/sys/pal/itron/task.rs index 94beb50a2541b..5da0c5917885f 100644 --- a/library/std/src/sys/pal/itron/task.rs +++ b/library/std/src/sys/pal/itron/task.rs @@ -1,23 +1,20 @@ -use super::{ - abi, - error::{fail, fail_aborting, ItronError}, -}; - +use super::abi; +use super::error::{fail, fail_aborting, ItronError}; use crate::mem::MaybeUninit; -/// Get the ID of the task in Running state. Panics on failure. +/// Gets the ID of the task in Running state. Panics on failure. #[inline] pub fn current_task_id() -> abi::ID { try_current_task_id().unwrap_or_else(|e| fail(e, &"get_tid")) } -/// Get the ID of the task in Running state. Aborts on failure. +/// Gets the ID of the task in Running state. Aborts on failure. #[inline] pub fn current_task_id_aborting() -> abi::ID { try_current_task_id().unwrap_or_else(|e| fail_aborting(e, &"get_tid")) } -/// Get the ID of the task in Running state. +/// Gets the ID of the task in Running state. #[inline] pub fn try_current_task_id() -> Result { unsafe { @@ -27,13 +24,13 @@ pub fn try_current_task_id() -> Result { } } -/// Get the specified task's priority. Panics on failure. +/// Gets the specified task's priority. Panics on failure. #[inline] pub fn task_priority(task: abi::ID) -> abi::PRI { try_task_priority(task).unwrap_or_else(|e| fail(e, &"get_pri")) } -/// Get the specified task's priority. +/// Gets the specified task's priority. #[inline] pub fn try_task_priority(task: abi::ID) -> Result { unsafe { diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index 205226ce1da80..01e69afa99e13 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -1,22 +1,17 @@ //! Thread implementation backed by μITRON tasks. Assumes `acre_tsk` and //! `exd_tsk` are available. -use super::{ - abi, - error::{expect_success, expect_success_aborting, ItronError}, - task, - time::dur2reltims, -}; -use crate::{ - cell::UnsafeCell, - ffi::CStr, - hint, io, - mem::ManuallyDrop, - num::NonZero, - ptr::NonNull, - sync::atomic::{AtomicUsize, Ordering}, - sys::thread_local_dtor::run_dtors, - time::Duration, -}; + +use super::error::{expect_success, expect_success_aborting, ItronError}; +use super::time::dur2reltims; +use super::{abi, task}; +use crate::cell::UnsafeCell; +use crate::ffi::CStr; +use crate::mem::ManuallyDrop; +use crate::num::NonZero; +use crate::ptr::NonNull; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::time::Duration; +use crate::{hint, io}; pub struct Thread { p_inner: NonNull, @@ -116,7 +111,7 @@ impl Thread { // Run TLS destructors now because they are not // called automatically for terminated tasks. - unsafe { run_dtors() }; + unsafe { crate::sys::thread_local::destructors::run() }; let old_lifecycle = inner .lifecycle @@ -308,7 +303,7 @@ impl Drop for Thread { } } -/// Terminate and delete the specified task. +/// Terminates and deletes the specified task. /// /// This function will abort if `deleted_task` refers to the calling task. /// @@ -337,7 +332,7 @@ unsafe fn terminate_and_delete_task(deleted_task: abi::ID) { expect_success_aborting(unsafe { abi::del_tsk(deleted_task) }, &"del_tsk"); } -/// Terminate and delete the calling task. +/// Terminates and deletes the calling task. /// /// Atomicity is not required - i.e., it can be assumed that other threads won't /// `ter_tsk` the calling task while this function is still in progress. (This diff --git a/library/std/src/sys/pal/itron/time.rs b/library/std/src/sys/pal/itron/time.rs index 427ea0d80e107..7976c27f4952b 100644 --- a/library/std/src/sys/pal/itron/time.rs +++ b/library/std/src/sys/pal/itron/time.rs @@ -1,5 +1,7 @@ -use super::{abi, error::expect_success}; -use crate::{mem::MaybeUninit, time::Duration}; +use super::abi; +use super::error::expect_success; +use crate::mem::MaybeUninit; +use crate::time::Duration; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct Instant(abi::SYSTIM); diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 8c75ac652998b..9be018c8a5312 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -76,54 +76,5 @@ cfg_if::cfg_if! { } } -#[cfg(not(test))] -cfg_if::cfg_if! { - if #[cfg(target_os = "android")] { - pub use self::android::log2f32; - pub use self::android::log2f64; - } else { - #[inline] - pub fn log2f32(n: f32) -> f32 { - unsafe { crate::intrinsics::log2f32(n) } - } - - #[inline] - pub fn log2f64(n: f64) -> f64 { - unsafe { crate::intrinsics::log2f64(n) } - } - } -} - -// Solaris/Illumos requires a wrapper around log, log2, and log10 functions -// because of their non-standard behavior (e.g., log(-n) returns -Inf instead -// of expected NaN). -#[cfg(not(test))] -#[cfg(any(target_os = "solaris", target_os = "illumos"))] -#[inline] -pub fn log_wrapper f64>(n: f64, log_fn: F) -> f64 { - if n.is_finite() { - if n > 0.0 { - log_fn(n) - } else if n == 0.0 { - f64::NEG_INFINITY // log(0) = -Inf - } else { - f64::NAN // log(-n) = NaN - } - } else if n.is_nan() { - n // log(NaN) = NaN - } else if n > 0.0 { - n // log(Inf) = Inf - } else { - f64::NAN // log(-Inf) = NaN - } -} - -#[cfg(not(test))] -#[cfg(not(any(target_os = "solaris", target_os = "illumos")))] -#[inline] -pub fn log_wrapper f64>(n: f64, log_fn: F) -> f64 { - log_fn(n) -} - #[cfg(not(target_os = "uefi"))] pub type RawOsError = i32; diff --git a/library/std/src/sys/pal/sgx/abi/mod.rs b/library/std/src/sys/pal/sgx/abi/mod.rs index 9508c38741551..d8836452e7555 100644 --- a/library/std/src/sys/pal/sgx/abi/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/mod.rs @@ -1,9 +1,10 @@ #![cfg_attr(test, allow(unused))] // RT initialization logic is not compiled for test -use crate::io::Write; use core::arch::global_asm; use core::sync::atomic::{AtomicUsize, Ordering}; +use crate::io::Write; + // runtime features pub(super) mod panic; mod reloc; diff --git a/library/std/src/sys/pal/sgx/abi/panic.rs b/library/std/src/sys/pal/sgx/abi/panic.rs index 229b3b3291f63..c06b97ee3674a 100644 --- a/library/std/src/sys/pal/sgx/abi/panic.rs +++ b/library/std/src/sys/pal/sgx/abi/panic.rs @@ -1,7 +1,6 @@ use super::usercalls::alloc::UserRef; -use crate::cmp; use crate::io::{self, Write}; -use crate::mem; +use crate::{cmp, mem}; extern "C" { fn take_debug_panic_buf_ptr() -> *mut u8; diff --git a/library/std/src/sys/pal/sgx/abi/tls/mod.rs b/library/std/src/sys/pal/sgx/abi/tls/mod.rs index 8a9ea4ac00df0..34fc2f20d2214 100644 --- a/library/std/src/sys/pal/sgx/abi/tls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/tls/mod.rs @@ -2,10 +2,9 @@ mod sync_bitset; use self::sync_bitset::*; use crate::cell::Cell; -use crate::mem; use crate::num::NonZero; -use crate::ptr; use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::{mem, ptr}; #[cfg(target_pointer_width = "64")] const USIZE_BITS: usize = 64; @@ -95,8 +94,8 @@ impl Tls { #[allow(unused)] pub unsafe fn activate_persistent(self: Box) { // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition. - unsafe { set_tls_ptr(core::ptr::addr_of!(*self) as _) }; - mem::forget(self); + let ptr = Box::into_raw(self).cast_const().cast::(); + unsafe { set_tls_ptr(ptr) }; } unsafe fn current<'a>() -> &'a Tls { diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs index f99cea360f1f4..298095257396a 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs @@ -1,18 +1,16 @@ #![allow(unused)] +use fortanix_sgx_abi::*; + +use super::super::mem::{is_enclave_range, is_user_range}; use crate::arch::asm; use crate::cell::UnsafeCell; -use crate::cmp; use crate::convert::TryInto; -use crate::intrinsics; -use crate::mem; +use crate::mem::{self, ManuallyDrop}; use crate::ops::{CoerceUnsized, Deref, DerefMut, Index, IndexMut}; use crate::ptr::{self, NonNull}; -use crate::slice; use crate::slice::SliceIndex; - -use super::super::mem::{is_enclave_range, is_user_range}; -use fortanix_sgx_abi::*; +use crate::{cmp, intrinsics, slice}; /// A type that can be safely read from or written to userspace. /// @@ -67,7 +65,7 @@ pub unsafe trait UserSafe { /// Equivalent to `mem::align_of::`. fn align_of() -> usize; - /// Construct a pointer to `Self` given a memory range in user space. + /// Constructs a pointer to `Self` given a memory range in user space. /// /// N.B., this takes a size, not a length! /// @@ -77,7 +75,7 @@ pub unsafe trait UserSafe { /// correct size and is correctly aligned and points to the right type. unsafe fn from_raw_sized_unchecked(ptr: *mut u8, size: usize) -> *mut Self; - /// Construct a pointer to `Self` given a memory range. + /// Constructs a pointer to `Self` given a memory range. /// /// N.B., this takes a size, not a length! /// @@ -176,6 +174,7 @@ unsafe impl UserSafe for [T] { /// are used solely to indicate intent: a mutable reference is for writing to /// user memory, an immutable reference for reading from user memory. #[unstable(feature = "sgx_platform", issue = "56975")] +#[repr(transparent)] pub struct UserRef(UnsafeCell); /// An owned type in userspace memory. `User` is equivalent to `Box` in /// enclave memory. Access to the memory is only allowed by copying to avoid @@ -266,9 +265,7 @@ where /// Converts this value into a raw pointer. The value will no longer be /// automatically freed. pub fn into_raw(self) -> *mut T { - let ret = self.0; - mem::forget(self); - ret.as_ptr() as _ + ManuallyDrop::new(self).0.as_ptr() as _ } } @@ -277,7 +274,7 @@ impl User where T: UserSafe, { - /// Allocate space for `T` in user memory. + /// Allocates space for `T` in user memory. pub fn uninitialized() -> Self { Self::new_uninit_bytes(mem::size_of::()) } @@ -288,7 +285,7 @@ impl User<[T]> where [T]: UserSafe, { - /// Allocate space for a `[T]` of `n` elements in user memory. + /// Allocates space for a `[T]` of `n` elements in user memory. pub fn uninitialized(n: usize) -> Self { Self::new_uninit_bytes(n * mem::size_of::()) } diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs index e19e843267a90..def1ccdf81ac0 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs @@ -171,10 +171,12 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult { unsafe { raw::wait(event_mask, timeout).from_sgx_result() } } -/// This function makes an effort to wait for a non-spurious event at least as -/// long as `duration`. Note that in general there is no guarantee about accuracy -/// of time and timeouts in SGX model. The enclave runner serving usercalls may -/// lie about current time and/or ignore timeout values. +/// Makes an effort to wait for a non-spurious event at least as long as +/// `duration`. +/// +/// Note that in general there is no guarantee about accuracy of time and +/// timeouts in SGX model. The enclave runner serving usercalls may lie about +/// current time and/or ignore timeout values. /// /// Once the event is observed, `should_wake_up` will be used to determine /// whether or not the event was spurious. diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/tests.rs b/library/std/src/sys/pal/sgx/abi/usercalls/tests.rs index 58b8eb215d73c..ef824d35f4a16 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/tests.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/tests.rs @@ -1,5 +1,4 @@ -use super::alloc::User; -use super::alloc::{copy_from_userspace, copy_to_userspace}; +use super::alloc::{copy_from_userspace, copy_to_userspace, User}; #[test] fn test_copy_to_userspace_function() { diff --git a/library/std/src/sys/pal/sgx/alloc.rs b/library/std/src/sys/pal/sgx/alloc.rs index 0c7bf9a9201f2..f68ede9fcf012 100644 --- a/library/std/src/sys/pal/sgx/alloc.rs +++ b/library/std/src/sys/pal/sgx/alloc.rs @@ -1,9 +1,9 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::ptr; use core::sync::atomic::{AtomicBool, Ordering}; use super::abi::mem as sgx_mem; use super::waitqueue::SpinMutex; +use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::ptr; // Using a SpinMutex because we never want to exit the enclave waiting for the // allocator. diff --git a/library/std/src/sys/pal/sgx/args.rs b/library/std/src/sys/pal/sgx/args.rs index ef4176c4ac0f0..a72a041da6cc9 100644 --- a/library/std/src/sys/pal/sgx/args.rs +++ b/library/std/src/sys/pal/sgx/args.rs @@ -1,10 +1,10 @@ -use super::abi::usercalls::{alloc, raw::ByteBuffer}; +use super::abi::usercalls::alloc; +use super::abi::usercalls::raw::ByteBuffer; use crate::ffi::OsString; -use crate::fmt; -use crate::slice; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sys::os_str::Buf; use crate::sys_common::FromInner; +use crate::{fmt, slice}; #[cfg_attr(test, linkage = "available_externally")] #[export_name = "_ZN16__rust_internals3std3sys3sgx4args4ARGSE"] diff --git a/library/std/src/sys/pal/sgx/fd.rs b/library/std/src/sys/pal/sgx/fd.rs index b3686d0e28328..c41b527cff798 100644 --- a/library/std/src/sys/pal/sgx/fd.rs +++ b/library/std/src/sys/pal/sgx/fd.rs @@ -2,7 +2,7 @@ use fortanix_sgx_abi::Fd; use super::abi::usercalls; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::mem; +use crate::mem::ManuallyDrop; use crate::sys::{AsInner, FromInner, IntoInner}; #[derive(Debug)] @@ -21,9 +21,7 @@ impl FileDesc { /// Extracts the actual file descriptor without closing it. pub fn into_raw(self) -> Fd { - let fd = self.fd; - mem::forget(self); - fd + ManuallyDrop::new(self).fd } pub fn read(&self, buf: &mut [u8]) -> io::Result { @@ -70,9 +68,7 @@ impl AsInner for FileDesc { impl IntoInner for FileDesc { fn into_inner(self) -> Fd { - let fd = self.fd; - mem::forget(self); - fd + ManuallyDrop::new(self).fd } } diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index d30976ec15149..851ab9b9f9767 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -26,7 +26,6 @@ pub mod pipe; pub mod process; pub mod stdio; pub mod thread; -pub mod thread_local_key; pub mod thread_parking; pub mod time; pub mod waitqueue; diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/pal/sgx/net.rs index 68a2d5eded2da..f2e751c51194d 100644 --- a/library/std/src/sys/pal/sgx/net.rs +++ b/library/std/src/sys/pal/sgx/net.rs @@ -1,13 +1,11 @@ -use crate::error; -use crate::fmt; +use super::abi::usercalls; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sync::Arc; use crate::sys::fd::FileDesc; use crate::sys::{sgx_ineffective, unsupported, AsInner, FromInner, IntoInner, TryIntoInner}; use crate::time::Duration; - -use super::abi::usercalls; +use crate::{error, fmt}; const DEFAULT_FAKE_TTL: u32 = 64; diff --git a/library/std/src/sys/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs index 86f4c7d3d56d6..46af710aa396c 100644 --- a/library/std/src/sys/pal/sgx/os.rs +++ b/library/std/src/sys/pal/sgx/os.rs @@ -3,16 +3,12 @@ use fortanix_sgx_abi::{Error, RESULT_SUCCESS}; use crate::collections::HashMap; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::io; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; -use crate::str; use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::Mutex; -use crate::sync::Once; +use crate::sync::{Mutex, Once}; use crate::sys::{decode_error_kind, sgx_ineffective, unsupported}; -use crate::vec; +use crate::{fmt, io, str, vec}; pub fn errno() -> i32 { RESULT_SUCCESS @@ -157,13 +153,13 @@ pub fn getenv(k: &OsStr) -> Option { get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned()) } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let (k, v) = (k.to_owned(), v.to_owned()); create_env_store().lock().unwrap().insert(k, v); Ok(()) } -pub fn unsetenv(k: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { if let Some(env) = get_env_store() { env.lock().unwrap().remove(k); } diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index 7d271e6d2b65d..cecd53c352c5a 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -1,12 +1,12 @@ #![cfg_attr(test, allow(dead_code))] // why is this necessary? + +use super::abi::usercalls; use super::unsupported; use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::time::Duration; -use super::abi::usercalls; - pub struct Thread(task_queue::JoinHandle); pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; @@ -15,7 +15,7 @@ pub use self::task_queue::JoinNotifier; mod task_queue { use super::wait_notify; - use crate::sync::{Mutex, MutexGuard, Once}; + use crate::sync::{Mutex, MutexGuard}; pub type JoinHandle = wait_notify::Waiter; @@ -28,12 +28,12 @@ mod task_queue { } pub(super) struct Task { - p: Box, + p: Box, done: JoinNotifier, } impl Task { - pub(super) fn new(p: Box) -> (Task, JoinHandle) { + pub(super) fn new(p: Box) -> (Task, JoinHandle) { let (done, recv) = wait_notify::new(); let done = JoinNotifier(Some(done)); (Task { p, done }, recv) @@ -45,18 +45,12 @@ mod task_queue { } } - #[cfg_attr(test, linkage = "available_externally")] - #[export_name = "_ZN16__rust_internals3std3sys3sgx6thread15TASK_QUEUE_INITE"] - static TASK_QUEUE_INIT: Once = Once::new(); #[cfg_attr(test, linkage = "available_externally")] #[export_name = "_ZN16__rust_internals3std3sys3sgx6thread10TASK_QUEUEE"] - static mut TASK_QUEUE: Option>> = None; + static TASK_QUEUE: Mutex> = Mutex::new(Vec::new()); pub(super) fn lock() -> MutexGuard<'static, Vec> { - unsafe { - TASK_QUEUE_INIT.call_once(|| TASK_QUEUE = Some(Default::default())); - TASK_QUEUE.as_ref().unwrap().lock().unwrap() - } + TASK_QUEUE.lock().unwrap() } } @@ -101,7 +95,7 @@ pub mod wait_notify { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, p: Box) -> io::Result { + pub unsafe fn new(_stack: usize, p: Box) -> io::Result { let mut queue_lock = task_queue::lock(); unsafe { usercalls::launch_thread()? }; let (task, handle) = task_queue::Task::new(p); diff --git a/library/std/src/sys/pal/sgx/thread_parking.rs b/library/std/src/sys/pal/sgx/thread_parking.rs index 0006cd4f1be25..660624ea9c3b8 100644 --- a/library/std/src/sys/pal/sgx/thread_parking.rs +++ b/library/std/src/sys/pal/sgx/thread_parking.rs @@ -1,7 +1,8 @@ +use fortanix_sgx_abi::{EV_UNPARK, WAIT_INDEFINITE}; + use super::abi::usercalls; use crate::io::ErrorKind; use crate::time::Duration; -use fortanix_sgx_abi::{EV_UNPARK, WAIT_INDEFINITE}; pub type ThreadId = fortanix_sgx_abi::Tcs; diff --git a/library/std/src/sys/pal/sgx/waitqueue/mod.rs b/library/std/src/sys/pal/sgx/waitqueue/mod.rs index f5668a9493fb5..bd114523fe80b 100644 --- a/library/std/src/sys/pal/sgx/waitqueue/mod.rs +++ b/library/std/src/sys/pal/sgx/waitqueue/mod.rs @@ -16,17 +16,15 @@ mod tests; mod spin_mutex; mod unsafe_list; -use crate::num::NonZero; -use crate::ops::{Deref, DerefMut}; -use crate::panic::{self, AssertUnwindSafe}; -use crate::time::Duration; - -use super::abi::thread; -use super::abi::usercalls; use fortanix_sgx_abi::{Tcs, EV_UNPARK, WAIT_INDEFINITE}; pub use self::spin_mutex::{try_lock_or_false, SpinMutex, SpinMutexGuard}; use self::unsafe_list::{UnsafeList, UnsafeListEntry}; +use super::abi::{thread, usercalls}; +use crate::num::NonZero; +use crate::ops::{Deref, DerefMut}; +use crate::panic::{self, AssertUnwindSafe}; +use crate::time::Duration; /// An queue entry in a `WaitQueue`. struct WaitEntry { diff --git a/library/std/src/sys/pal/solid/abi/fs.rs b/library/std/src/sys/pal/solid/abi/fs.rs index 49526f4c9cd4d..394be15b0064b 100644 --- a/library/std/src/sys/pal/solid/abi/fs.rs +++ b/library/std/src/sys/pal/solid/abi/fs.rs @@ -1,10 +1,12 @@ //! `solid_fs.h` -use crate::os::raw::{c_char, c_int, c_uchar}; + pub use libc::{ ino_t, off_t, stat, time_t, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, SEEK_CUR, SEEK_END, SEEK_SET, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFMT, S_IFREG, S_IWRITE, }; +use crate::os::raw::{c_char, c_int, c_uchar}; + pub const O_ACCMODE: c_int = 0x3; pub const SOLID_MAX_PATH: usize = 256; diff --git a/library/std/src/sys/pal/solid/abi/mod.rs b/library/std/src/sys/pal/solid/abi/mod.rs index 8440d572cfbd3..62cd86236bbd3 100644 --- a/library/std/src/sys/pal/solid/abi/mod.rs +++ b/library/std/src/sys/pal/solid/abi/mod.rs @@ -3,7 +3,6 @@ use crate::os::raw::c_int; mod fs; pub mod sockets; pub use self::fs::*; - // `solid_types.h` pub use super::itron::abi::{ER, ER_ID, E_TMOUT, ID}; diff --git a/library/std/src/sys/pal/solid/abi/sockets.rs b/library/std/src/sys/pal/solid/abi/sockets.rs index 11c430360ce88..3c9e3f9ffb95d 100644 --- a/library/std/src/sys/pal/solid/abi/sockets.rs +++ b/library/std/src/sys/pal/solid/abi/sockets.rs @@ -1,6 +1,7 @@ -use crate::os::raw::{c_char, c_uint, c_void}; pub use libc::{c_int, c_long, size_t, ssize_t, timeval}; +use crate::os::raw::{c_char, c_uint, c_void}; + pub const SOLID_NET_ERR_BASE: c_int = -2000; pub const EINPROGRESS: c_int = SOLID_NET_ERR_BASE - libc::EINPROGRESS; diff --git a/library/std/src/sys/pal/solid/alloc.rs b/library/std/src/sys/pal/solid/alloc.rs index d013bd8761003..4cf60ac9b2e23 100644 --- a/library/std/src/sys/pal/solid/alloc.rs +++ b/library/std/src/sys/pal/solid/alloc.rs @@ -1,7 +1,5 @@ -use crate::{ - alloc::{GlobalAlloc, Layout, System}, - sys::common::alloc::{realloc_fallback, MIN_ALIGN}, -}; +use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { diff --git a/library/std/src/sys/pal/solid/error.rs b/library/std/src/sys/pal/solid/error.rs index 547b4f3a9840e..66c4d8a0ea27a 100644 --- a/library/std/src/sys/pal/solid/error.rs +++ b/library/std/src/sys/pal/solid/error.rs @@ -1,8 +1,7 @@ +pub use self::itron::error::{expect_success, ItronError as SolidError}; use super::{abi, itron, net}; use crate::io::ErrorKind; -pub use self::itron::error::{expect_success, ItronError as SolidError}; - /// Describe the specified SOLID error code. Returns `None` if it's an /// undefined error code. /// diff --git a/library/std/src/sys/pal/solid/fs.rs b/library/std/src/sys/pal/solid/fs.rs index a6c1336109ad7..8179ec8821a38 100644 --- a/library/std/src/sys/pal/solid/fs.rs +++ b/library/std/src/sys/pal/solid/fs.rs @@ -1,18 +1,15 @@ use super::{abi, error}; -use crate::{ - ffi::{CStr, CString, OsStr, OsString}, - fmt, - io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}, - mem::MaybeUninit, - os::raw::{c_int, c_short}, - os::solid::ffi::OsStrExt, - path::{Path, PathBuf}, - sync::Arc, - sys::time::SystemTime, - sys::unsupported, -}; - -pub use crate::sys_common::fs::try_exists; +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::fmt; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem::MaybeUninit; +use crate::os::raw::{c_int, c_short}; +use crate::os::solid::ffi::OsStrExt; +use crate::path::{Path, PathBuf}; +use crate::sync::Arc; +use crate::sys::time::SystemTime; +use crate::sys::unsupported; +pub use crate::sys_common::fs::exists; /// A file descriptor. #[derive(Clone, Copy)] diff --git a/library/std/src/sys/pal/solid/io.rs b/library/std/src/sys/pal/solid/io.rs index a862bb7870264..4b1f788a492c5 100644 --- a/library/std/src/sys/pal/solid/io.rs +++ b/library/std/src/sys/pal/solid/io.rs @@ -1,8 +1,8 @@ -use crate::marker::PhantomData; -use crate::slice; +use libc::c_void; use super::abi::sockets::iovec; -use libc::c_void; +use crate::marker::PhantomData; +use crate::slice; #[derive(Copy, Clone)] #[repr(transparent)] diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 3f6ff37903ac6..cbf34286878fe 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] #![allow(missing_docs, nonstandard_style)] -#![deny(unsafe_op_in_unsafe_fn)] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod abi; @@ -32,10 +32,7 @@ pub mod pipe; #[path = "../unsupported/process.rs"] pub mod process; pub mod stdio; -pub use self::itron::thread; -pub mod thread_local_dtor; -pub mod thread_local_key; -pub use self::itron::thread_parking; +pub use self::itron::{thread, thread_parking}; pub mod time; // SAFETY: must be called only once during runtime initialization. diff --git a/library/std/src/sys/pal/solid/net.rs b/library/std/src/sys/pal/solid/net.rs index 5bd339849e9dc..b6a31395095d9 100644 --- a/library/std/src/sys/pal/solid/net.rs +++ b/library/std/src/sys/pal/solid/net.rs @@ -1,19 +1,15 @@ -use super::abi; -use crate::{ - cmp, - ffi::CStr, - io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}, - mem, - net::{Shutdown, SocketAddr}, - os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}, - ptr, str, - sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}, - sys_common::{FromInner, IntoInner}, - time::Duration, -}; +use libc::{c_int, c_void, size_t}; use self::netc::{sockaddr, socklen_t, MSG_PEEK}; -use libc::{c_int, c_void, size_t}; +use super::abi; +use crate::ffi::CStr; +use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; +use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys_common::{FromInner, IntoInner}; +use crate::time::Duration; +use crate::{cmp, mem, ptr, str}; pub mod netc { pub use super::super::abi::sockets::*; diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index ef35d8788a236..d8afcb91f67f2 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -1,20 +1,14 @@ -use super::unsupported; +use core::slice::memchr; + +use super::{error, itron, unsupported}; use crate::error::Error as StdError; use crate::ffi::{CStr, OsStr, OsString}; -use crate::fmt; -use crate::io; -use crate::os::{ - raw::{c_char, c_int}, - solid::ffi::{OsStrExt, OsStringExt}, -}; +use crate::os::raw::{c_char, c_int}; +use crate::os::solid::ffi::{OsStrExt, OsStringExt}; use crate::path::{self, PathBuf}; use crate::sync::{PoisonError, RwLock}; use crate::sys::common::small_c_string::run_with_cstr; -use crate::vec; - -use super::{error, itron}; - -use core::slice::memchr; +use crate::{fmt, io, vec}; // `solid` directly maps `errno`s to μITRON error codes. impl itron::error::ItronError { @@ -191,7 +185,7 @@ pub fn getenv(k: &OsStr) -> Option { .flatten() } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { run_with_cstr(k.as_bytes(), &|k| { run_with_cstr(v.as_bytes(), &|v| { let _guard = ENV_LOCK.write(); @@ -200,7 +194,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { }) } -pub fn unsetenv(n: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { run_with_cstr(n.as_bytes(), &|nbuf| { let _guard = ENV_LOCK.write(); cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) diff --git a/library/std/src/sys/pal/solid/thread_local_dtor.rs b/library/std/src/sys/pal/solid/thread_local_dtor.rs deleted file mode 100644 index 26918a4fcb012..0000000000000 --- a/library/std/src/sys/pal/solid/thread_local_dtor.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![cfg(target_thread_local)] -#![unstable(feature = "thread_local_internals", issue = "none")] - -// Simplify dtor registration by using a list of destructors. - -use super::{abi, itron::task}; -use crate::cell::{Cell, RefCell}; - -#[thread_local] -static REGISTERED: Cell = Cell::new(false); - -#[thread_local] -static DTORS: RefCell> = RefCell::new(Vec::new()); - -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - if !REGISTERED.get() { - let tid = task::current_task_id_aborting(); - // Register `tls_dtor` to make sure the TLS destructors are called - // for tasks created by other means than `std::thread` - unsafe { abi::SOLID_TLS_AddDestructor(tid as i32, tls_dtor) }; - REGISTERED.set(true); - } - - match DTORS.try_borrow_mut() { - Ok(mut dtors) => dtors.push((t, dtor)), - Err(_) => rtabort!("global allocator may not use TLS"), - } -} - -pub unsafe fn run_dtors() { - let mut list = DTORS.take(); - while !list.is_empty() { - for (ptr, dtor) in list { - unsafe { dtor(ptr) }; - } - - list = DTORS.take(); - } -} - -unsafe extern "C" fn tls_dtor(_unused: *mut u8) { - unsafe { run_dtors() }; -} diff --git a/library/std/src/sys/pal/solid/thread_local_key.rs b/library/std/src/sys/pal/solid/thread_local_key.rs deleted file mode 100644 index b37bf99969887..0000000000000 --- a/library/std/src/sys/pal/solid/thread_local_key.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub type Key = usize; - -#[inline] -pub unsafe fn create(_dtor: Option) -> Key { - panic!("should not be used on the solid target"); -} - -#[inline] -pub unsafe fn set(_key: Key, _value: *mut u8) { - panic!("should not be used on the solid target"); -} - -#[inline] -pub unsafe fn get(_key: Key) -> *mut u8 { - panic!("should not be used on the solid target"); -} - -#[inline] -pub unsafe fn destroy(_key: Key) { - panic!("should not be used on the solid target"); -} diff --git a/library/std/src/sys/pal/solid/time.rs b/library/std/src/sys/pal/solid/time.rs index f83f1644fe854..3f9bbb0b63cdb 100644 --- a/library/std/src/sys/pal/solid/time.rs +++ b/library/std/src/sys/pal/solid/time.rs @@ -1,7 +1,8 @@ -use super::{abi, error::expect_success}; -use crate::{mem::MaybeUninit, time::Duration}; - +use super::abi; +use super::error::expect_success; pub use super::itron::time::Instant; +use crate::mem::MaybeUninit; +use crate::time::Duration; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct SystemTime(abi::time_t); diff --git a/library/std/src/sys/pal/teeos/alloc.rs b/library/std/src/sys/pal/teeos/alloc.rs index e236819aa2388..b280d1dd76f7a 100644 --- a/library/std/src/sys/pal/teeos/alloc.rs +++ b/library/std/src/sys/pal/teeos/alloc.rs @@ -11,9 +11,9 @@ unsafe impl GlobalAlloc for System { // Also see and // . if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::malloc(layout.size()) as *mut u8 + unsafe { libc::malloc(layout.size()) as *mut u8 } } else { - aligned_malloc(&layout) + unsafe { aligned_malloc(&layout) } } } @@ -21,11 +21,11 @@ unsafe impl GlobalAlloc for System { unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { // See the comment above in `alloc` for why this check looks the way it does. if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::calloc(layout.size(), 1) as *mut u8 + unsafe { libc::calloc(layout.size(), 1) as *mut u8 } } else { - let ptr = self.alloc(layout); + let ptr = unsafe { self.alloc(layout) }; if !ptr.is_null() { - ptr::write_bytes(ptr, 0, layout.size()); + unsafe { ptr::write_bytes(ptr, 0, layout.size()) }; } ptr } @@ -33,15 +33,15 @@ unsafe impl GlobalAlloc for System { #[inline] unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - libc::free(ptr as *mut libc::c_void) + unsafe { libc::free(ptr as *mut libc::c_void) } } #[inline] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { if layout.align() <= MIN_ALIGN && layout.align() <= new_size { - libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 + unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 } } else { - realloc_fallback(self, ptr, layout, new_size) + unsafe { realloc_fallback(self, ptr, layout, new_size) } } } } @@ -52,6 +52,6 @@ unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { // posix_memalign requires that the alignment be a multiple of `sizeof(void*)`. // Since these are all powers of 2, we can just use max. let align = layout.align().max(crate::mem::size_of::()); - let ret = libc::posix_memalign(&mut out, align, layout.size()); + let ret = unsafe { libc::posix_memalign(&mut out, align, layout.size()) }; if ret != 0 { ptr::null_mut() } else { out as *mut u8 } } diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index 6dd465a12ed49..adefd1bb42c8d 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -2,7 +2,7 @@ //! //! This module contains the facade (aka platform-specific) implementations of //! OS level functionality for Teeos. -#![allow(unsafe_op_in_unsafe_fn)] +#![deny(unsafe_op_in_unsafe_fn)] #![allow(unused_variables)] #![allow(dead_code)] @@ -27,9 +27,6 @@ pub mod process; mod rand; pub mod stdio; pub mod thread; -pub mod thread_local_dtor; -#[path = "../unix/thread_local_key.rs"] -pub mod thread_local_key; #[allow(non_upper_case_globals)] #[path = "../unix/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index e54a92f01f86b..82cade771b513 100644 --- a/library/std/src/sys/pal/teeos/os.rs +++ b/library/std/src/sys/pal/teeos/os.rs @@ -2,14 +2,11 @@ use core::marker::PhantomData; +use super::unsupported; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::io; -use crate::path; use crate::path::PathBuf; - -use super::unsupported; +use crate::{fmt, io, path}; pub fn errno() -> i32 { unsafe { (*libc::__errno_location()) as i32 } @@ -109,11 +106,11 @@ pub fn getenv(_: &OsStr) -> Option { None } -pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { Err(io::Error::new(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } -pub fn unsetenv(_: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { Err(io::Error::new(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } diff --git a/library/std/src/sys/pal/teeos/stdio.rs b/library/std/src/sys/pal/teeos/stdio.rs index 9ca04f2927323..67e251812da7f 100644 --- a/library/std/src/sys/pal/teeos/stdio.rs +++ b/library/std/src/sys/pal/teeos/stdio.rs @@ -1,8 +1,9 @@ #![deny(unsafe_op_in_unsafe_fn)] -use crate::io; use core::arch::asm; +use crate::io; + pub struct Stdin; pub struct Stdout; pub struct Stderr; diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs index f4723b2ea46bf..15c65240ddd2a 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -1,13 +1,9 @@ -use core::convert::TryInto; - -use crate::cmp; use crate::ffi::CStr; -use crate::io; -use crate::mem; +use crate::mem::{self, ManuallyDrop}; use crate::num::NonZero; -use crate::ptr; use crate::sys::os; use crate::time::Duration; +use crate::{cmp, io, ptr}; pub const DEFAULT_MIN_STACK_SIZE: usize = 8 * 1024; @@ -28,22 +24,24 @@ impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements pub unsafe fn new(stack: usize, p: Box) -> io::Result { let p = Box::into_raw(Box::new(p)); - let mut native: libc::pthread_t = mem::zeroed(); - let mut attr: libc::pthread_attr_t = mem::zeroed(); - assert_eq!(libc::pthread_attr_init(&mut attr), 0); + let mut native: libc::pthread_t = unsafe { mem::zeroed() }; + let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; + assert_eq!(unsafe { libc::pthread_attr_init(&mut attr) }, 0); assert_eq!( - libc::pthread_attr_settee( - &mut attr, - libc::TEESMP_THREAD_ATTR_CA_INHERIT, - libc::TEESMP_THREAD_ATTR_TASK_ID_INHERIT, - libc::TEESMP_THREAD_ATTR_HAS_SHADOW, - ), + unsafe { + libc::pthread_attr_settee( + &mut attr, + libc::TEESMP_THREAD_ATTR_CA_INHERIT, + libc::TEESMP_THREAD_ATTR_TASK_ID_INHERIT, + libc::TEESMP_THREAD_ATTR_HAS_SHADOW, + ) + }, 0, ); let stack_size = cmp::max(stack, min_stack_size(&attr)); - match libc::pthread_attr_setstacksize(&mut attr, stack_size) { + match unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) } { 0 => {} n => { assert_eq!(n, libc::EINVAL); @@ -54,7 +52,7 @@ impl Thread { let page_size = os::page_size(); let stack_size = (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); - assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0); + assert_eq!(unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) }, 0); } }; @@ -62,12 +60,12 @@ impl Thread { // Note: if the thread creation fails and this assert fails, then p will // be leaked. However, an alternative design could cause double-free // which is clearly worse. - assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + assert_eq!(unsafe { libc::pthread_attr_destroy(&mut attr) }, 0); return if ret != 0 { // The thread failed to start and as a result p was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. - drop(Box::from_raw(p)); + drop(unsafe { Box::from_raw(p) }); Err(io::Error::from_raw_os_error(ret)) } else { // The new thread will start running earliest after the next yield. @@ -113,11 +111,9 @@ impl Thread { /// must join, because no pthread_detach supported pub fn join(self) { - unsafe { - let ret = libc::pthread_join(self.id, ptr::null_mut()); - mem::forget(self); - assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); - } + let id = self.into_id(); + let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; + assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); } pub fn id(&self) -> libc::pthread_t { @@ -125,9 +121,7 @@ impl Thread { } pub fn into_id(self) -> libc::pthread_t { - let id = self.id; - mem::forget(self); - id + ManuallyDrop::new(self).id } } diff --git a/library/std/src/sys/pal/teeos/thread_local_dtor.rs b/library/std/src/sys/pal/teeos/thread_local_dtor.rs deleted file mode 100644 index 5c6bc4d675011..0000000000000 --- a/library/std/src/sys/pal/teeos/thread_local_dtor.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - use crate::sys_common::thread_local_dtor::register_dtor_fallback; - register_dtor_fallback(t, dtor); -} diff --git a/library/std/src/sys/pal/uefi/args.rs b/library/std/src/sys/pal/uefi/args.rs index 18a69afa7d91b..bdf6f5a0c1c34 100644 --- a/library/std/src/sys/pal/uefi/args.rs +++ b/library/std/src/sys/pal/uefi/args.rs @@ -3,10 +3,9 @@ use r_efi::protocols::loaded_image; use super::helpers; use crate::env::current_exe; use crate::ffi::OsString; -use crate::fmt; use crate::iter::Iterator; use crate::mem::size_of; -use crate::vec; +use crate::{fmt, vec}; pub struct Args { parsed_args_list: vec::IntoIter, @@ -76,7 +75,7 @@ impl DoubleEndedIterator for Args { /// This implementation is based on what is defined in Section 3.4 of /// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_Spec_2_0.pdf) /// -/// Return None in the following cases: +/// Returns None in the following cases: /// - Invalid UTF-16 (unpaired surrogate) /// - Empty/improper arguments fn parse_lp_cmd_line(code_units: &[u16]) -> Option> { diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 23aa4da14a763..4031d33ba8066 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -12,20 +12,29 @@ use r_efi::efi::{self, Guid}; use r_efi::protocols::{device_path, device_path_to_text}; -use crate::ffi::OsString; +use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_io_error}; use crate::mem::{size_of, MaybeUninit}; -use crate::os::uefi::{self, env::boot_services, ffi::OsStringExt}; +use crate::os::uefi::env::boot_services; +use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; +use crate::os::uefi::{self}; use crate::ptr::NonNull; use crate::slice; use crate::sync::atomic::{AtomicPtr, Ordering}; use crate::sys_common::wstr::WStrUnits; +type BootInstallMultipleProtocolInterfaces = + unsafe extern "efiapi" fn(_: *mut r_efi::efi::Handle, _: ...) -> r_efi::efi::Status; + +type BootUninstallMultipleProtocolInterfaces = + unsafe extern "efiapi" fn(_: r_efi::efi::Handle, _: ...) -> r_efi::efi::Status; + const BOOT_SERVICES_UNAVAILABLE: io::Error = const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); -/// Locate Handles with a particular Protocol GUID -/// Implemented using `EFI_BOOT_SERVICES.LocateHandles()` +/// Locates Handles with a particular Protocol GUID. +/// +/// Implemented using `EFI_BOOT_SERVICES.LocateHandles()`. /// /// Returns an array of [Handles](r_efi::efi::Handle) that support a specified protocol. pub(crate) fn locate_handles(mut guid: Guid) -> io::Result>> { @@ -142,8 +151,9 @@ pub(crate) unsafe fn close_event(evt: NonNull) -> io::Result if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } -/// Get the Protocol for current system handle. -/// Note: Some protocols need to be manually freed. It is the callers responsibility to do so. +/// Gets the Protocol for current system handle. +/// +/// Note: Some protocols need to be manually freed. It is the caller's responsibility to do so. pub(crate) fn image_handle_protocol(protocol_guid: Guid) -> io::Result> { let system_handle = uefi::env::try_image_handle().ok_or(io::const_io_error!( io::ErrorKind::NotFound, @@ -214,10 +224,199 @@ pub(crate) fn device_path_to_text(path: NonNull) -> io::R Err(io::const_io_error!(io::ErrorKind::NotFound, "No device path to text protocol found")) } -/// Get RuntimeServices +/// Gets RuntimeServices. pub(crate) fn runtime_services() -> Option> { let system_table: NonNull = crate::os::uefi::env::try_system_table()?.cast(); let runtime_services = unsafe { (*system_table.as_ptr()).runtime_services }; NonNull::new(runtime_services) } + +pub(crate) struct DevicePath(NonNull); + +impl DevicePath { + pub(crate) fn from_text(p: &OsStr) -> io::Result { + fn inner( + p: &OsStr, + protocol: NonNull, + ) -> io::Result { + let path_vec = p.encode_wide().chain(Some(0)).collect::>(); + if path_vec[..path_vec.len() - 1].contains(&0) { + return Err(const_io_error!( + io::ErrorKind::InvalidInput, + "strings passed to UEFI cannot contain NULs", + )); + } + + let path = + unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) }; + + NonNull::new(path).map(DevicePath).ok_or_else(|| { + const_io_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path") + }) + } + + static LAST_VALID_HANDLE: AtomicPtr = + AtomicPtr::new(crate::ptr::null_mut()); + + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { + if let Ok(protocol) = open_protocol::( + handle, + r_efi::protocols::device_path_from_text::PROTOCOL_GUID, + ) { + return inner(p, protocol); + } + } + + let handles = locate_handles(r_efi::protocols::device_path_from_text::PROTOCOL_GUID)?; + for handle in handles { + if let Ok(protocol) = open_protocol::( + handle, + r_efi::protocols::device_path_from_text::PROTOCOL_GUID, + ) { + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); + return inner(p, protocol); + } + } + + io::Result::Err(const_io_error!( + io::ErrorKind::NotFound, + "DevicePathFromText Protocol not found" + )) + } + + pub(crate) fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { + self.0.as_ptr() + } +} + +impl Drop for DevicePath { + fn drop(&mut self) { + if let Some(bt) = boot_services() { + let bt: NonNull = bt.cast(); + unsafe { + ((*bt.as_ptr()).free_pool)(self.0.as_ptr() as *mut crate::ffi::c_void); + } + } + } +} + +pub(crate) struct OwnedProtocol { + guid: r_efi::efi::Guid, + handle: NonNull, + protocol: *mut T, +} + +impl OwnedProtocol { + // FIXME: Consider using unsafe trait for matching protocol with guid + pub(crate) unsafe fn create(protocol: T, mut guid: r_efi::efi::Guid) -> io::Result { + let bt: NonNull = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let protocol: *mut T = Box::into_raw(Box::new(protocol)); + let mut handle: r_efi::efi::Handle = crate::ptr::null_mut(); + + // FIXME: Move into r-efi once extended_varargs_abi_support is stablized + let func: BootInstallMultipleProtocolInterfaces = + unsafe { crate::mem::transmute((*bt.as_ptr()).install_multiple_protocol_interfaces) }; + + let r = unsafe { + func( + &mut handle, + &mut guid as *mut _ as *mut crate::ffi::c_void, + protocol as *mut crate::ffi::c_void, + crate::ptr::null_mut() as *mut crate::ffi::c_void, + ) + }; + + if r.is_error() { + drop(unsafe { Box::from_raw(protocol) }); + return Err(crate::io::Error::from_raw_os_error(r.as_usize())); + }; + + let handle = NonNull::new(handle) + .ok_or(io::const_io_error!(io::ErrorKind::Uncategorized, "found null handle"))?; + + Ok(Self { guid, handle, protocol }) + } + + pub(crate) fn handle(&self) -> NonNull { + self.handle + } +} + +impl Drop for OwnedProtocol { + fn drop(&mut self) { + // Do not deallocate a runtime protocol + if let Some(bt) = boot_services() { + let bt: NonNull = bt.cast(); + // FIXME: Move into r-efi once extended_varargs_abi_support is stablized + let func: BootUninstallMultipleProtocolInterfaces = unsafe { + crate::mem::transmute((*bt.as_ptr()).uninstall_multiple_protocol_interfaces) + }; + let status = unsafe { + func( + self.handle.as_ptr(), + &mut self.guid as *mut _ as *mut crate::ffi::c_void, + self.protocol as *mut crate::ffi::c_void, + crate::ptr::null_mut() as *mut crate::ffi::c_void, + ) + }; + + // Leak the protocol in case uninstall fails + if status == r_efi::efi::Status::SUCCESS { + let _ = unsafe { Box::from_raw(self.protocol) }; + } + } + } +} + +impl AsRef for OwnedProtocol { + fn as_ref(&self) -> &T { + unsafe { self.protocol.as_ref().unwrap() } + } +} + +pub(crate) struct OwnedTable { + layout: crate::alloc::Layout, + ptr: *mut T, +} + +impl OwnedTable { + pub(crate) fn from_table_header(hdr: &r_efi::efi::TableHeader) -> Self { + let header_size = hdr.header_size as usize; + let layout = crate::alloc::Layout::from_size_align(header_size, 8).unwrap(); + let ptr = unsafe { crate::alloc::alloc(layout) as *mut T }; + Self { layout, ptr } + } + + pub(crate) const fn as_ptr(&self) -> *const T { + self.ptr + } + + pub(crate) const fn as_mut_ptr(&self) -> *mut T { + self.ptr + } +} + +impl OwnedTable { + pub(crate) fn from_table(tbl: *const r_efi::efi::SystemTable) -> Self { + let hdr = unsafe { (*tbl).hdr }; + + let owned_tbl = Self::from_table_header(&hdr); + unsafe { + crate::ptr::copy_nonoverlapping( + tbl as *const u8, + owned_tbl.as_mut_ptr() as *mut u8, + hdr.header_size as usize, + ) + }; + + owned_tbl + } +} + +impl Drop for OwnedTable { + fn drop(&mut self) { + unsafe { crate::alloc::dealloc(self.ptr as *mut u8, self.layout) }; + } +} diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 48b74df138439..851bcea4c1e43 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -11,6 +11,7 @@ //! //! [`OsStr`]: crate::ffi::OsStr //! [`OsString`]: crate::ffi::OsString +#![forbid(unsafe_op_in_unsafe_fn)] pub mod alloc; pub mod args; @@ -24,12 +25,9 @@ pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/process.rs"] pub mod process; pub mod stdio; pub mod thread; -#[path = "../unsupported/thread_local_key.rs"] -pub mod thread_local_key; pub mod time; mod helpers; @@ -103,9 +101,10 @@ pub const fn unsupported_err() -> std_io::Error { } pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { - use crate::io::ErrorKind; use r_efi::efi::Status; + use crate::io::ErrorKind; + match r_efi::efi::Status::from_usize(code) { Status::ALREADY_STARTED | Status::COMPROMISED_DATA diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 58838c5876ebd..4d2d7264704b2 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -1,14 +1,14 @@ +use r_efi::efi::protocols::{device_path, loaded_image_device_path}; +use r_efi::efi::Status; + use super::{helpers, unsupported, RawOsError}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::io; use crate::marker::PhantomData; use crate::os::uefi; use crate::path::{self, PathBuf}; use crate::ptr::NonNull; -use r_efi::efi::protocols::{device_path, loaded_image_device_path}; -use r_efi::efi::Status; +use crate::{fmt, io}; pub fn errno() -> RawOsError { 0 @@ -203,11 +203,11 @@ pub fn getenv(_: &OsStr) -> Option { None } -pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } -pub fn unsetenv(_: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs new file mode 100644 index 0000000000000..fdc5f5d7e4fea --- /dev/null +++ b/library/std/src/sys/pal/uefi/process.rs @@ -0,0 +1,684 @@ +use r_efi::protocols::simple_text_output; + +use super::helpers; +pub use crate::ffi::OsString as EnvKey; +use crate::ffi::{OsStr, OsString}; +use crate::num::{NonZero, NonZeroI32}; +use crate::path::Path; +use crate::sys::fs::File; +use crate::sys::pipe::AnonPipe; +use crate::sys::unsupported; +use crate::sys_common::process::{CommandEnv, CommandEnvs}; +use crate::{fmt, io}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +#[derive(Debug)] +pub struct Command { + prog: OsString, + stdout: Option, + stderr: Option, +} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + +#[derive(Copy, Clone, Debug)] +pub enum Stdio { + Inherit, + Null, + MakePipe, +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + Command { prog: program.to_os_string(), stdout: None, stderr: None } + } + + // FIXME: Implement arguments as reverse of parsing algorithm + pub fn arg(&mut self, _arg: &OsStr) { + panic!("unsupported") + } + + pub fn env_mut(&mut self) -> &mut CommandEnv { + panic!("unsupported") + } + + pub fn cwd(&mut self, _dir: &OsStr) { + panic!("unsupported") + } + + pub fn stdin(&mut self, _stdin: Stdio) { + panic!("unsupported") + } + + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } + + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } + + pub fn get_program(&self) -> &OsStr { + self.prog.as_ref() + } + + pub fn get_args(&self) -> CommandArgs<'_> { + panic!("unsupported") + } + + pub fn get_envs(&self) -> CommandEnvs<'_> { + panic!("unsupported") + } + + pub fn get_current_dir(&self) -> Option<&Path> { + None + } + + pub fn spawn( + &mut self, + _default: Stdio, + _needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + unsupported() + } + + fn create_pipe( + s: Stdio, + ) -> io::Result>> { + match s { + Stdio::MakePipe => unsafe { + helpers::OwnedProtocol::create( + uefi_command_internal::PipeProtocol::new(), + simple_text_output::PROTOCOL_GUID, + ) + } + .map(Some), + Stdio::Null => unsafe { + helpers::OwnedProtocol::create( + uefi_command_internal::PipeProtocol::null(), + simple_text_output::PROTOCOL_GUID, + ) + } + .map(Some), + Stdio::Inherit => Ok(None), + } + } + + pub fn output(&mut self) -> io::Result<(ExitStatus, Vec, Vec)> { + let mut cmd = uefi_command_internal::Image::load_image(&self.prog)?; + + // Setup Stdout + let stdout = self.stdout.unwrap_or(Stdio::MakePipe); + let stdout = Self::create_pipe(stdout)?; + if let Some(con) = stdout { + cmd.stdout_init(con) + } else { + cmd.stdout_inherit() + }; + + // Setup Stderr + let stderr = self.stderr.unwrap_or(Stdio::MakePipe); + let stderr = Self::create_pipe(stderr)?; + if let Some(con) = stderr { + cmd.stderr_init(con) + } else { + cmd.stderr_inherit() + }; + + let stat = cmd.start_image()?; + + let stdout = cmd.stdout()?; + let stderr = cmd.stderr()?; + + Ok((ExitStatus(stat), stdout, stderr)) + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From for Stdio { + fn from(_: io::Stdout) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +impl From for Stdio { + fn from(_: io::Stderr) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +impl From for Stdio { + fn from(_file: File) -> Stdio { + // FIXME: This is wrong. + // Instead, the Stdio we have here should be a unit struct. + panic!("unsupported") + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[non_exhaustive] +pub struct ExitStatus(r_efi::efi::Status); + +impl ExitStatus { + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + if self.0 == r_efi::efi::Status::SUCCESS { Ok(()) } else { Err(ExitStatusError(self.0)) } + } + + pub fn code(&self) -> Option { + Some(self.0.as_usize() as i32) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let err_str = super::os::error_string(self.0.as_usize()); + write!(f, "{}", err_str) + } +} + +impl Default for ExitStatus { + fn default() -> Self { + ExitStatus(r_efi::efi::Status::SUCCESS) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct ExitStatusError(r_efi::efi::Status); + +impl fmt::Debug for ExitStatusError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let err_str = super::os::error_string(self.0.as_usize()); + write!(f, "{}", err_str) + } +} + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option> { + NonZeroI32::new(self.0.as_usize() as i32) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(bool); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(false); + pub const FAILURE: ExitCode = ExitCode(true); + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + +impl From for ExitCode { + fn from(code: u8) -> Self { + match code { + 0 => Self::SUCCESS, + 1..=255 => Self::FAILURE, + } + } +} + +pub struct Process(!); + +impl Process { + pub fn id(&self) -> u32 { + self.0 + } + + pub fn kill(&mut self) -> io::Result<()> { + self.0 + } + + pub fn wait(&mut self) -> io::Result { + self.0 + } + + pub fn try_wait(&mut self) -> io::Result> { + self.0 + } +} + +pub struct CommandArgs<'a> { + iter: crate::slice::Iter<'a, OsString>, +} + +impl<'a> Iterator for CommandArgs<'a> { + type Item = &'a OsStr; + + fn next(&mut self) -> Option<&'a OsStr> { + self.iter.next().map(|x| x.as_ref()) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a> ExactSizeIterator for CommandArgs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +impl<'a> fmt::Debug for CommandArgs<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter.clone()).finish() + } +} + +#[allow(dead_code)] +mod uefi_command_internal { + use r_efi::protocols::{loaded_image, simple_text_output}; + + use super::super::helpers; + use crate::ffi::{OsStr, OsString}; + use crate::io::{self, const_io_error}; + use crate::mem::MaybeUninit; + use crate::os::uefi::env::{boot_services, image_handle, system_table}; + use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; + use crate::ptr::NonNull; + use crate::slice; + use crate::sys::pal::uefi::helpers::OwnedTable; + use crate::sys_common::wstr::WStrUnits; + + pub struct Image { + handle: NonNull, + stdout: Option>, + stderr: Option>, + st: OwnedTable, + args: Option>, + } + + impl Image { + pub fn load_image(p: &OsStr) -> io::Result { + let path = helpers::DevicePath::from_text(p)?; + let boot_services: NonNull = boot_services() + .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))? + .cast(); + let mut child_handle: MaybeUninit = MaybeUninit::uninit(); + let image_handle = image_handle(); + + let r = unsafe { + ((*boot_services.as_ptr()).load_image)( + r_efi::efi::Boolean::FALSE, + image_handle.as_ptr(), + path.as_ptr(), + crate::ptr::null_mut(), + 0, + child_handle.as_mut_ptr(), + ) + }; + + if r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + let child_handle = unsafe { child_handle.assume_init() }; + let child_handle = NonNull::new(child_handle).unwrap(); + + let loaded_image: NonNull = + helpers::open_protocol(child_handle, loaded_image::PROTOCOL_GUID).unwrap(); + let st = OwnedTable::from_table(unsafe { (*loaded_image.as_ptr()).system_table }); + + Ok(Self { handle: child_handle, stdout: None, stderr: None, st, args: None }) + } + } + + pub fn start_image(&mut self) -> io::Result { + self.update_st_crc32()?; + + // Use our system table instead of the default one + let loaded_image: NonNull = + helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); + unsafe { + (*loaded_image.as_ptr()).system_table = self.st.as_mut_ptr(); + } + + let boot_services: NonNull = boot_services() + .ok_or_else(|| const_io_error!(io::ErrorKind::NotFound, "Boot Services not found"))? + .cast(); + let mut exit_data_size: usize = 0; + let mut exit_data: MaybeUninit<*mut u16> = MaybeUninit::uninit(); + + let r = unsafe { + ((*boot_services.as_ptr()).start_image)( + self.handle.as_ptr(), + &mut exit_data_size, + exit_data.as_mut_ptr(), + ) + }; + + // Drop exitdata + if exit_data_size != 0 { + unsafe { + let exit_data = exit_data.assume_init(); + ((*boot_services.as_ptr()).free_pool)(exit_data as *mut crate::ffi::c_void); + } + } + + Ok(r) + } + + fn set_stdout( + &mut self, + handle: r_efi::efi::Handle, + protocol: *mut simple_text_output::Protocol, + ) { + unsafe { + (*self.st.as_mut_ptr()).console_out_handle = handle; + (*self.st.as_mut_ptr()).con_out = protocol; + } + } + + fn set_stderr( + &mut self, + handle: r_efi::efi::Handle, + protocol: *mut simple_text_output::Protocol, + ) { + unsafe { + (*self.st.as_mut_ptr()).standard_error_handle = handle; + (*self.st.as_mut_ptr()).std_err = protocol; + } + } + + pub fn stdout_init(&mut self, protocol: helpers::OwnedProtocol) { + self.set_stdout( + protocol.handle().as_ptr(), + protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol, + ); + self.stdout = Some(protocol); + } + + pub fn stdout_inherit(&mut self) { + let st: NonNull = system_table().cast(); + unsafe { self.set_stdout((*st.as_ptr()).console_out_handle, (*st.as_ptr()).con_out) } + } + + pub fn stderr_init(&mut self, protocol: helpers::OwnedProtocol) { + self.set_stderr( + protocol.handle().as_ptr(), + protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol, + ); + self.stderr = Some(protocol); + } + + pub fn stderr_inherit(&mut self) { + let st: NonNull = system_table().cast(); + unsafe { self.set_stderr((*st.as_ptr()).standard_error_handle, (*st.as_ptr()).std_err) } + } + + pub fn stderr(&self) -> io::Result> { + match &self.stderr { + Some(stderr) => stderr.as_ref().utf8(), + None => Ok(Vec::new()), + } + } + + pub fn stdout(&self) -> io::Result> { + match &self.stdout { + Some(stdout) => stdout.as_ref().utf8(), + None => Ok(Vec::new()), + } + } + + pub fn set_args(&mut self, args: &OsStr) { + let loaded_image: NonNull = + helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); + + let mut args = args.encode_wide().collect::>(); + let args_size = (crate::mem::size_of::() * args.len()) as u32; + + unsafe { + (*loaded_image.as_ptr()).load_options = + args.as_mut_ptr() as *mut crate::ffi::c_void; + (*loaded_image.as_ptr()).load_options_size = args_size; + } + + self.args = Some(args); + } + + fn update_st_crc32(&mut self) -> io::Result<()> { + let bt: NonNull = boot_services().unwrap().cast(); + let st_size = unsafe { (*self.st.as_ptr()).hdr.header_size as usize }; + let mut crc32: u32 = 0; + + // Set crc to 0 before calcuation + unsafe { + (*self.st.as_mut_ptr()).hdr.crc32 = 0; + } + + let r = unsafe { + ((*bt.as_ptr()).calculate_crc32)( + self.st.as_mut_ptr() as *mut crate::ffi::c_void, + st_size, + &mut crc32, + ) + }; + + if r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + unsafe { + (*self.st.as_mut_ptr()).hdr.crc32 = crc32; + } + Ok(()) + } + } + } + + impl Drop for Image { + fn drop(&mut self) { + if let Some(bt) = boot_services() { + let bt: NonNull = bt.cast(); + unsafe { + ((*bt.as_ptr()).unload_image)(self.handle.as_ptr()); + } + } + } + } + + #[repr(C)] + pub struct PipeProtocol { + reset: simple_text_output::ProtocolReset, + output_string: simple_text_output::ProtocolOutputString, + test_string: simple_text_output::ProtocolTestString, + query_mode: simple_text_output::ProtocolQueryMode, + set_mode: simple_text_output::ProtocolSetMode, + set_attribute: simple_text_output::ProtocolSetAttribute, + clear_screen: simple_text_output::ProtocolClearScreen, + set_cursor_position: simple_text_output::ProtocolSetCursorPosition, + enable_cursor: simple_text_output::ProtocolEnableCursor, + mode: *mut simple_text_output::Mode, + _buffer: Vec, + } + + impl PipeProtocol { + pub fn new() -> Self { + let mode = Box::new(simple_text_output::Mode { + max_mode: 0, + mode: 0, + attribute: 0, + cursor_column: 0, + cursor_row: 0, + cursor_visible: r_efi::efi::Boolean::FALSE, + }); + Self { + reset: Self::reset, + output_string: Self::output_string, + test_string: Self::test_string, + query_mode: Self::query_mode, + set_mode: Self::set_mode, + set_attribute: Self::set_attribute, + clear_screen: Self::clear_screen, + set_cursor_position: Self::set_cursor_position, + enable_cursor: Self::enable_cursor, + mode: Box::into_raw(mode), + _buffer: Vec::new(), + } + } + + pub fn null() -> Self { + let mode = Box::new(simple_text_output::Mode { + max_mode: 0, + mode: 0, + attribute: 0, + cursor_column: 0, + cursor_row: 0, + cursor_visible: r_efi::efi::Boolean::FALSE, + }); + Self { + reset: Self::reset_null, + output_string: Self::output_string_null, + test_string: Self::test_string, + query_mode: Self::query_mode, + set_mode: Self::set_mode, + set_attribute: Self::set_attribute, + clear_screen: Self::clear_screen, + set_cursor_position: Self::set_cursor_position, + enable_cursor: Self::enable_cursor, + mode: Box::into_raw(mode), + _buffer: Vec::new(), + } + } + + pub fn utf8(&self) -> io::Result> { + OsString::from_wide(&self._buffer) + .into_string() + .map(Into::into) + .map_err(|_| const_io_error!(io::ErrorKind::Other, "utf8 conversion failed")) + } + + extern "efiapi" fn reset( + proto: *mut simple_text_output::Protocol, + _: r_efi::efi::Boolean, + ) -> r_efi::efi::Status { + let proto: *mut PipeProtocol = proto.cast(); + unsafe { + (*proto)._buffer.clear(); + } + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn reset_null( + _: *mut simple_text_output::Protocol, + _: r_efi::efi::Boolean, + ) -> r_efi::efi::Status { + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn output_string( + proto: *mut simple_text_output::Protocol, + buf: *mut r_efi::efi::Char16, + ) -> r_efi::efi::Status { + let proto: *mut PipeProtocol = proto.cast(); + let buf_len = unsafe { + if let Some(x) = WStrUnits::new(buf) { + x.count() + } else { + return r_efi::efi::Status::INVALID_PARAMETER; + } + }; + let buf_slice = unsafe { slice::from_raw_parts(buf, buf_len) }; + + unsafe { + (*proto)._buffer.extend_from_slice(buf_slice); + }; + + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn output_string_null( + _: *mut simple_text_output::Protocol, + _: *mut r_efi::efi::Char16, + ) -> r_efi::efi::Status { + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn test_string( + _: *mut simple_text_output::Protocol, + _: *mut r_efi::efi::Char16, + ) -> r_efi::efi::Status { + r_efi::efi::Status::SUCCESS + } + + extern "efiapi" fn query_mode( + _: *mut simple_text_output::Protocol, + _: usize, + _: *mut usize, + _: *mut usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn set_mode( + _: *mut simple_text_output::Protocol, + _: usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn set_attribute( + _: *mut simple_text_output::Protocol, + _: usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn clear_screen( + _: *mut simple_text_output::Protocol, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn set_cursor_position( + _: *mut simple_text_output::Protocol, + _: usize, + _: usize, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + + extern "efiapi" fn enable_cursor( + _: *mut simple_text_output::Protocol, + _: r_efi::efi::Boolean, + ) -> r_efi::efi::Status { + r_efi::efi::Status::UNSUPPORTED + } + } + + impl Drop for PipeProtocol { + fn drop(&mut self) { + unsafe { + let _ = Box::from_raw(self.mode); + } + } + } +} diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs index edc736978a123..7d4006ff4b2f7 100644 --- a/library/std/src/sys/pal/uefi/thread.rs +++ b/library/std/src/sys/pal/uefi/thread.rs @@ -7,7 +7,7 @@ use crate::time::Duration; pub struct Thread(!); -pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; +pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs index 76562cf9f51c0..495ff2dc930ed 100644 --- a/library/std/src/sys/pal/uefi/time.rs +++ b/library/std/src/sys/pal/uefi/time.rs @@ -59,11 +59,12 @@ impl SystemTime { } pub(crate) mod system_time_internal { + use r_efi::efi::{RuntimeServices, Time}; + use super::super::helpers; use super::*; use crate::mem::MaybeUninit; use crate::ptr::NonNull; - use r_efi::efi::{RuntimeServices, Time}; pub fn now() -> Option { let runtime_services: NonNull = helpers::runtime_services()?; @@ -114,13 +115,14 @@ pub(crate) mod system_time_internal { } pub(crate) mod instant_internal { + use r_efi::protocols::timestamp; + use super::super::helpers; use super::*; use crate::mem::MaybeUninit; use crate::ptr::NonNull; use crate::sync::atomic::{AtomicPtr, Ordering}; use crate::sys_common::mul_div_u64; - use r_efi::protocols::timestamp; const NS_PER_SEC: u64 = 1_000_000_000; @@ -173,10 +175,6 @@ pub(crate) mod instant_internal { #[cfg(target_arch = "x86_64")] fn timestamp_rdtsc() -> Option { - if !crate::arch::x86_64::has_cpuid() { - return None; - } - static FREQUENCY: crate::sync::OnceLock = crate::sync::OnceLock::new(); // Get Frequency in Mhz @@ -198,10 +196,6 @@ pub(crate) mod instant_internal { #[cfg(target_arch = "x86")] fn timestamp_rdtsc() -> Option { - if !crate::arch::x86::has_cpuid() { - return None; - } - static FREQUENCY: crate::sync::OnceLock = crate::sync::OnceLock::new(); let freq = FREQUENCY diff --git a/library/std/src/sys/pal/unix/alloc.rs b/library/std/src/sys/pal/unix/alloc.rs index 2f908e3d0e956..625ba5247f111 100644 --- a/library/std/src/sys/pal/unix/alloc.rs +++ b/library/std/src/sys/pal/unix/alloc.rs @@ -59,27 +59,25 @@ unsafe impl GlobalAlloc for System { } cfg_if::cfg_if! { - // We use posix_memalign wherever possible, but not all targets have that function. + // We use posix_memalign wherever possible, but some targets have very incomplete POSIX coverage + // so we need a fallback for those. if #[cfg(any( - target_os = "redox", - target_os = "espidf", target_os = "horizon", target_os = "vita", ))] { #[inline] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { - libc::memalign(layout.align(), layout.size()) as *mut u8 + unsafe { libc::memalign(layout.align(), layout.size()) as *mut u8 } } } else { #[inline] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { let mut out = ptr::null_mut(); - // We prefer posix_memalign over aligned_malloc since with aligned_malloc, - // implementations are making almost arbitrary choices for which alignments are - // "supported", making it hard to use. For instance, some implementations require the - // size to be a multiple of the alignment (wasi emmalloc), while others require the - // alignment to be at least the pointer size (Illumos, macOS) -- which may or may not be - // standards-compliant, but that does not help us. + // We prefer posix_memalign over aligned_alloc since it is more widely available, and + // since with aligned_alloc, implementations are making almost arbitrary choices for + // which alignments are "supported", making it hard to use. For instance, some + // implementations require the size to be a multiple of the alignment (wasi emmalloc), + // while others require the alignment to be at least the pointer size (Illumos, macOS). // posix_memalign only has one, clear requirement: that the alignment be a multiple of // `sizeof(void*)`. Since these are all powers of 2, we can just use max. let align = layout.align().max(crate::mem::size_of::()); diff --git a/library/std/src/sys/pal/unix/android.rs b/library/std/src/sys/pal/unix/android.rs deleted file mode 100644 index 0f704994f550a..0000000000000 --- a/library/std/src/sys/pal/unix/android.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! Android ABI-compatibility module -//! -//! The ABI of Android has changed quite a bit over time, and std attempts to be -//! both forwards and backwards compatible as much as possible. We want to -//! always work with the most recent version of Android, but we also want to -//! work with older versions of Android for whenever projects need to. -//! -//! Our current minimum supported Android version is `android-9`, e.g., Android -//! with API level 9. We then in theory want to work on that and all future -//! versions of Android! -//! -//! Some of the detection here is done at runtime via `dlopen` and -//! introspection. Other times no detection is performed at all and we just -//! provide a fallback implementation as some versions of Android we support -//! don't have the function. -//! -//! You'll find more details below about why each compatibility shim is needed. - -#![cfg(target_os = "android")] - -use libc::{c_int, sighandler_t}; - -use super::weak::weak; - -// The `log2` and `log2f` functions apparently appeared in android-18, or at -// least you can see they're not present in the android-17 header [1] and they -// are present in android-18 [2]. -// -// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms -// /android-17/arch-arm/usr/include/math.h -// [2]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms -// /android-18/arch-arm/usr/include/math.h -// -// Note that these shims are likely less precise than directly calling `log2`, -// but hopefully that should be enough for now... -// -// Note that mathematically, for any arbitrary `y`: -// -// log_2(x) = log_y(x) / log_y(2) -// = log_y(x) / (1 / log_2(y)) -// = log_y(x) * log_2(y) -// -// Hence because `ln` (log_e) is available on all Android we just choose `y = e` -// and get: -// -// log_2(x) = ln(x) * log_2(e) - -#[cfg(not(test))] -pub fn log2f32(f: f32) -> f32 { - f.ln() * crate::f32::consts::LOG2_E -} - -#[cfg(not(test))] -pub fn log2f64(f: f64) -> f64 { - f.ln() * crate::f64::consts::LOG2_E -} - -// Back in the day [1] the `signal` function was just an inline wrapper -// around `bsd_signal`, but starting in API level android-20 the `signal` -// symbols was introduced [2]. Finally, in android-21 the API `bsd_signal` was -// removed [3]. -// -// Basically this means that if we want to be binary compatible with multiple -// Android releases (oldest being 9 and newest being 21) then we need to check -// for both symbols and not actually link against either. -// -// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms -// /android-18/arch-arm/usr/include/signal.h -// [2]: https://chromium.googlesource.com/android_tools/+/fbd420/ndk_experimental -// /platforms/android-20/arch-arm -// /usr/include/signal.h -// [3]: https://chromium.googlesource.com/android_tools/+/20ee6d/ndk/platforms -// /android-21/arch-arm/usr/include/signal.h -pub unsafe fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t { - weak!(fn signal(c_int, sighandler_t) -> sighandler_t); - weak!(fn bsd_signal(c_int, sighandler_t) -> sighandler_t); - - let f = signal.get().or_else(|| bsd_signal.get()); - let f = f.expect("neither `signal` nor `bsd_signal` symbols found"); - f(signum, handler) -} diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs index db2ec73148e38..9a37e1a0346d7 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/pal/unix/args.rs @@ -6,9 +6,8 @@ #![allow(dead_code)] // runtime init functions not used during testing use crate::ffi::{CStr, OsString}; -use crate::fmt; use crate::os::unix::ffi::OsStringExt; -use crate::vec; +use crate::{fmt, vec}; /// One-time global initialization. pub unsafe fn init(argc: isize, argv: *const *const u8) { @@ -183,7 +182,7 @@ mod imp { // Use `_NSGetArgc` and `_NSGetArgv` on Apple platforms. // // Even though these have underscores in their names, they've been available -// since since the first versions of both macOS and iOS, and are declared in +// since the first versions of both macOS and iOS, and are declared in // the header `crt_externs.h`. // // NOTE: This header was added to the iOS 13.0 SDK, which has been the source diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs index 1701717db597c..d8e239ee23ed5 100644 --- a/library/std/src/sys/pal/unix/fd.rs +++ b/library/std/src/sys/pal/unix/fd.rs @@ -3,12 +3,6 @@ #[cfg(test)] mod tests; -use crate::cmp; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::cvt; -use crate::sys_common::{AsInner, FromInner, IntoInner}; - #[cfg(any( target_os = "android", target_os = "linux", @@ -26,6 +20,12 @@ use libc::off64_t; )))] use libc::off_t as off64_t; +use crate::cmp; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::cvt; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + #[derive(Debug)] pub struct FileDesc(OwnedFd); @@ -82,6 +82,11 @@ const fn max_iov() -> usize { } impl FileDesc { + #[inline] + pub fn try_clone(&self) -> io::Result { + self.duplicate() + } + pub fn read(&self, buf: &mut [u8]) -> io::Result { let ret = cvt(unsafe { libc::read( @@ -120,6 +125,7 @@ impl FileDesc { (&mut me).read_to_end(buf) } + #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { #[cfg(not(any( all(target_os = "linux", not(target_env = "musl")), @@ -313,6 +319,7 @@ impl FileDesc { cfg!(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))) } + #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { #[cfg(not(any( all(target_os = "linux", not(target_env = "musl")), diff --git a/library/std/src/sys/pal/unix/fd/tests.rs b/library/std/src/sys/pal/unix/fd/tests.rs index 5d17e46786c79..c5301ce655787 100644 --- a/library/std/src/sys/pal/unix/fd/tests.rs +++ b/library/std/src/sys/pal/unix/fd/tests.rs @@ -1,6 +1,7 @@ +use core::mem::ManuallyDrop; + use super::{FileDesc, IoSlice}; use crate::os::unix::io::FromRawFd; -use core::mem::ManuallyDrop; #[test] fn limit_vector_count() { diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index fbbd40bfb796a..bdb83f0785784 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -4,34 +4,7 @@ #[cfg(test)] mod tests; -use crate::os::unix::prelude::*; - -use crate::ffi::{CStr, OsStr, OsString}; -use crate::fmt::{self, Write as _}; -use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; -use crate::path::{Path, PathBuf}; -use crate::ptr; -use crate::sync::Arc; -use crate::sys::common::small_c_string::run_path_with_cstr; -use crate::sys::fd::FileDesc; -use crate::sys::time::SystemTime; -use crate::sys::{cvt, cvt_r}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; - -#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_vendor = "apple"))] -use crate::sys::weak::syscall; -#[cfg(any(target_os = "android", target_os = "macos", target_os = "solaris"))] -use crate::sys::weak::weak; - -use libc::{c_int, mode_t}; - -#[cfg(any( - target_os = "solaris", - all(target_os = "linux", target_env = "gnu"), - target_vendor = "apple", -))] +#[cfg(all(target_os = "linux", target_env = "gnu"))] use libc::c_char; #[cfg(any( all(target_os = "linux", not(target_env = "musl")), @@ -77,6 +50,7 @@ use libc::readdir64_r; target_os = "hurd", )))] use libc::readdir_r as readdir64_r; +use libc::{c_int, mode_t}; #[cfg(target_os = "android")] use libc::{ dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64, @@ -101,7 +75,24 @@ use libc::{ ))] use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64}; -pub use crate::sys_common::fs::try_exists; +use crate::ffi::{CStr, OsStr, OsString}; +use crate::fmt::{self, Write as _}; +use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; +use crate::os::unix::prelude::*; +use crate::path::{Path, PathBuf}; +use crate::sync::Arc; +use crate::sys::common::small_c_string::run_path_with_cstr; +use crate::sys::fd::FileDesc; +use crate::sys::time::SystemTime; +#[cfg(all(target_os = "linux", target_env = "gnu"))] +use crate::sys::weak::syscall; +#[cfg(target_os = "android")] +use crate::sys::weak::weak; +use crate::sys::{cvt, cvt_r}; +pub use crate::sys_common::fs::exists; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::{mem, ptr}; pub struct File(FileDesc); @@ -467,15 +458,15 @@ impl FileAttr { #[cfg(target_os = "aix")] impl FileAttr { pub fn modified(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64)) + SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64) } pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64)) + SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64) } pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64)) + SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64) } } @@ -861,6 +852,7 @@ impl Drop for Dir { target_os = "espidf", target_os = "fuchsia", target_os = "horizon", + target_os = "vxworks", )))] { let fd = unsafe { libc::dirfd(self.0) }; @@ -1317,7 +1309,12 @@ impl File { } pub fn set_times(&self, times: FileTimes) -> io::Result<()> { - #[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))] + #[cfg(not(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "vxworks" + )))] let to_timespec = |time: Option| match time { Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_io_error!( @@ -1331,10 +1328,11 @@ impl File { None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), }; cfg_if::cfg_if! { - if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon"))] { + if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vxworks"))] { // Redox doesn't appear to support `UTIME_OMIT`. // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore // the same as for Redox. + // `futimens` and `UTIME_OMIT` are a work in progress for vxworks. let _ = times; Err(io::const_io_error!( io::ErrorKind::Unsupported, @@ -1481,29 +1479,33 @@ impl FromRawFd for File { impl fmt::Debug for File { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[cfg(any( - target_os = "linux", - target_os = "netbsd", - target_os = "illumos", - target_os = "solaris" - ))] + #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))] fn get_path(fd: c_int) -> Option { let mut p = PathBuf::from("/proc/self/fd"); p.push(&fd.to_string()); readlink(&p).ok() } - #[cfg(target_vendor = "apple")] + #[cfg(any(target_vendor = "apple", target_os = "netbsd"))] fn get_path(fd: c_int) -> Option { // FIXME: The use of PATH_MAX is generally not encouraged, but it - // is inevitable in this case because Apple targets define `fcntl` + // is inevitable in this case because Apple targets and NetBSD define `fcntl` // with `F_GETPATH` in terms of `MAXPATHLEN`, and there are no // alternatives. If a better method is invented, it should be used // instead. let mut buf = vec![0; libc::PATH_MAX as usize]; let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; if n == -1 { - return None; + cfg_if::cfg_if! { + if #[cfg(target_os = "netbsd")] { + // fallback to procfs as last resort + let mut p = PathBuf::from("/proc/self/fd"); + p.push(&fd.to_string()); + return readlink(&p).ok(); + } else { + return None; + } + } } let l = buf.iter().position(|&c| c == 0).unwrap(); buf.truncate(l as usize); @@ -1557,6 +1559,8 @@ impl fmt::Debug for File { target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", + target_os = "solaris", + target_os = "illumos", target_vendor = "apple", ))] fn get_mode(fd: c_int) -> Option<(bool, bool)> { @@ -1579,6 +1583,8 @@ impl fmt::Debug for File { target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", + target_os = "solaris", + target_os = "illumos", target_vendor = "apple", )))] fn get_mode(_fd: c_int) -> Option<(bool, bool)> { @@ -1745,19 +1751,6 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { // Android has `linkat` on newer versions, but we happen to know `link` // always has the correct behavior, so it's here as well. cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; - } else if #[cfg(any(target_os = "macos", target_os = "solaris"))] { - // MacOS (<=10.9) and Solaris 10 lack support for linkat while newer - // versions have it. We want to use linkat if it is available, so we use weak! - // to check. `linkat` is preferable to `link` because it gives us a flag to - // specify how symlinks should be handled. We pass 0 as the flags argument, - // meaning it shouldn't follow symlinks. - weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int); - - if let Some(f) = linkat.get() { - cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; - } else { - cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; - }; } else { // Where we can, use `linkat` instead of `link`; see the comment above // this one for details on why. @@ -1900,8 +1893,6 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { #[cfg(target_vendor = "apple")] pub fn copy(from: &Path, to: &Path) -> io::Result { - use crate::sync::atomic::{AtomicBool, Ordering}; - const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA; struct FreeOnDrop(libc::copyfile_state_t); @@ -1910,46 +1901,27 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { // The code below ensures that `FreeOnDrop` is never a null pointer unsafe { // `copyfile_state_free` returns -1 if the `to` or `from` files - // cannot be closed. However, this is not considered this an - // error. + // cannot be closed. However, this is not considered an error. libc::copyfile_state_free(self.0); } } } - // MacOS prior to 10.12 don't support `fclonefileat` - // We store the availability in a global to avoid unnecessary syscalls - static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true); - syscall! { - // Mirrors `libc::fclonefileat` - fn fclonefileat( - srcfd: libc::c_int, - dst_dirfd: libc::c_int, - dst: *const c_char, - flags: libc::c_int - ) -> libc::c_int - } - let (reader, reader_metadata) = open_from(from)?; - // Opportunistically attempt to create a copy-on-write clone of `from` - // using `fclonefileat`. - if HAS_FCLONEFILEAT.load(Ordering::Relaxed) { - let clonefile_result = run_path_with_cstr(to, &|to| { - cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) }) - }); - match clonefile_result { - Ok(_) => return Ok(reader_metadata.len()), - Err(err) => match err.raw_os_error() { - // `fclonefileat` will fail on non-APFS volumes, if the - // destination already exists, or if the source and destination - // are on different devices. In all these cases `fcopyfile` - // should succeed. - Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (), - Some(libc::ENOSYS) => HAS_FCLONEFILEAT.store(false, Ordering::Relaxed), - _ => return Err(err), - }, - } + let clonefile_result = run_path_with_cstr(to, &|to| { + cvt(unsafe { libc::fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) }) + }); + match clonefile_result { + Ok(_) => return Ok(reader_metadata.len()), + Err(e) => match e.raw_os_error() { + // `fclonefileat` will fail on non-APFS volumes, if the + // destination already exists, or if the source and destination + // are on different devices. In all these cases `fcopyfile` + // should succeed. + Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (), + _ => return Err(e), + }, } // Fall back to using `fcopyfile` if `fclonefileat` does not succeed. @@ -1992,6 +1964,7 @@ pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> { Ok(()) } +#[cfg(not(target_os = "vxworks"))] pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { run_path_with_cstr(path, &|path| { cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) @@ -1999,20 +1972,33 @@ pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { }) } +#[cfg(target_os = "vxworks")] +pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { + let (_, _, _) = (path, uid, gid); + Err(io::const_io_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks")) +} + #[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] pub fn chroot(dir: &Path) -> io::Result<()> { run_path_with_cstr(dir, &|dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ())) } +#[cfg(target_os = "vxworks")] +pub fn chroot(dir: &Path) -> io::Result<()> { + let _ = dir; + Err(io::const_io_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks")) +} + pub use remove_dir_impl::remove_dir_all; -// Fallback for REDOX, ESP-ID, Horizon, Vita and Miri +// Fallback for REDOX, ESP-ID, Horizon, Vita, Vxworks and Miri #[cfg(any( target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nto", + target_os = "vxworks", miri ))] mod remove_dir_impl { @@ -2026,9 +2012,15 @@ mod remove_dir_impl { target_os = "horizon", target_os = "vita", target_os = "nto", + target_os = "vxworks", miri )))] mod remove_dir_impl { + #[cfg(not(all(target_os = "linux", target_env = "gnu")))] + use libc::{fdopendir, openat, unlinkat}; + #[cfg(all(target_os = "linux", target_env = "gnu"))] + use libc::{fdopendir, openat64 as openat, unlinkat}; + use super::{lstat, Dir, DirEntry, InnerReadDir, ReadDir}; use crate::ffi::CStr; use crate::io; @@ -2038,57 +2030,6 @@ mod remove_dir_impl { use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::{cvt, cvt_r}; - #[cfg(not(any( - all(target_os = "linux", target_env = "gnu"), - all(target_os = "macos", not(target_arch = "aarch64")) - )))] - use libc::{fdopendir, openat, unlinkat}; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{fdopendir, openat64 as openat, unlinkat}; - #[cfg(all(target_os = "macos", not(target_arch = "aarch64")))] - use macos_weak::{fdopendir, openat, unlinkat}; - - #[cfg(all(target_os = "macos", not(target_arch = "aarch64")))] - mod macos_weak { - use crate::sys::weak::weak; - use libc::{c_char, c_int, DIR}; - - fn get_openat_fn() -> Option c_int> { - weak!(fn openat(c_int, *const c_char, c_int) -> c_int); - openat.get() - } - - pub fn has_openat() -> bool { - get_openat_fn().is_some() - } - - pub unsafe fn openat(dirfd: c_int, pathname: *const c_char, flags: c_int) -> c_int { - get_openat_fn().map(|openat| openat(dirfd, pathname, flags)).unwrap_or_else(|| { - crate::sys::pal::unix::os::set_errno(libc::ENOSYS); - -1 - }) - } - - pub unsafe fn fdopendir(fd: c_int) -> *mut DIR { - #[cfg(all(target_os = "macos", target_arch = "x86"))] - weak!(fn fdopendir(c_int) -> *mut DIR, "fdopendir$INODE64$UNIX2003"); - #[cfg(all(target_os = "macos", target_arch = "x86_64"))] - weak!(fn fdopendir(c_int) -> *mut DIR, "fdopendir$INODE64"); - fdopendir.get().map(|fdopendir| fdopendir(fd)).unwrap_or_else(|| { - crate::sys::pal::unix::os::set_errno(libc::ENOSYS); - crate::ptr::null_mut() - }) - } - - pub unsafe fn unlinkat(dirfd: c_int, pathname: *const c_char, flags: c_int) -> c_int { - weak!(fn unlinkat(c_int, *const c_char, c_int) -> c_int); - unlinkat.get().map(|unlinkat| unlinkat(dirfd, pathname, flags)).unwrap_or_else(|| { - crate::sys::pal::unix::os::set_errno(libc::ENOSYS); - -1 - }) - } - } - pub fn openat_nofollow_dironly(parent_fd: Option, p: &CStr) -> io::Result { let fd = cvt_r(|| unsafe { openat( @@ -2200,19 +2141,7 @@ mod remove_dir_impl { } } - #[cfg(not(all(target_os = "macos", not(target_arch = "aarch64"))))] pub fn remove_dir_all(p: &Path) -> io::Result<()> { remove_dir_all_modern(p) } - - #[cfg(all(target_os = "macos", not(target_arch = "aarch64")))] - pub fn remove_dir_all(p: &Path) -> io::Result<()> { - if macos_weak::has_openat() { - // openat() is available with macOS 10.10+, just like unlinkat() and fdopendir() - remove_dir_all_modern(p) - } else { - // fall back to classic implementation - crate::sys_common::fs::remove_dir_all(p) - } - } } diff --git a/library/std/src/sys/pal/unix/futex.rs b/library/std/src/sys/pal/unix/futex.rs index 26161a9af79d8..cc725045c4810 100644 --- a/library/std/src/sys/pal/unix/futex.rs +++ b/library/std/src/sys/pal/unix/futex.rs @@ -11,7 +11,12 @@ use crate::sync::atomic::AtomicU32; use crate::time::Duration; -/// Wait for a futex_wake operation to wake us. +/// An atomic for use as a futex that is at least 8-bits but may be larger. +pub type SmallAtomic = AtomicU32; +/// Must be the underlying type of SmallAtomic +pub type SmallPrimitive = u32; + +/// Waits for a `futex_wake` operation to wake us. /// /// Returns directly if the futex doesn't hold the expected value. /// @@ -82,7 +87,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - } } -/// Wake up one thread that's blocked on futex_wait on this futex. +/// Wakes up one thread that's blocked on `futex_wait` on this futex. /// /// Returns true if this actually woke up such a thread, /// or false if no thread was waiting on this futex. @@ -95,7 +100,7 @@ pub fn futex_wake(futex: &AtomicU32) -> bool { unsafe { libc::syscall(libc::SYS_futex, ptr, op, 1) > 0 } } -/// Wake up all threads that are waiting on futex_wait on this futex. +/// Wakes up all threads that are waiting on `futex_wait` on this futex. #[cfg(any(target_os = "linux", target_os = "android"))] pub fn futex_wake_all(futex: &AtomicU32) { let ptr = futex as *const AtomicU32; diff --git a/library/std/src/sys/pal/unix/io.rs b/library/std/src/sys/pal/unix/io.rs index 29c340dd34942..181c35a971eca 100644 --- a/library/std/src/sys/pal/unix/io.rs +++ b/library/std/src/sys/pal/unix/io.rs @@ -1,9 +1,9 @@ +use libc::{c_void, iovec}; + use crate::marker::PhantomData; use crate::os::fd::{AsFd, AsRawFd}; use crate::slice; -use libc::{c_void, iovec}; - #[derive(Copy, Clone)] #[repr(transparent)] pub struct IoSlice<'a> { diff --git a/library/std/src/sys/pal/unix/kernel_copy.rs b/library/std/src/sys/pal/unix/kernel_copy.rs index 18acd5ecccd5c..a671383cb7957 100644 --- a/library/std/src/sys/pal/unix/kernel_copy.rs +++ b/library/std/src/sys/pal/unix/kernel_copy.rs @@ -42,6 +42,12 @@ //! progress, they can hit a performance cliff. //! * complexity +#[cfg(not(any(all(target_os = "linux", target_env = "gnu"), target_os = "hurd")))] +use libc::sendfile as sendfile64; +#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "hurd"))] +use libc::sendfile64; +use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV}; + use crate::cmp::min; use crate::fs::{File, Metadata}; use crate::io::copy::generic_copy; @@ -54,16 +60,12 @@ use crate::net::TcpStream; use crate::os::unix::fs::FileTypeExt; use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use crate::os::unix::net::UnixStream; +use crate::pipe::{PipeReader, PipeWriter}; use crate::process::{ChildStderr, ChildStdin, ChildStdout}; use crate::ptr; use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use crate::sys::cvt; use crate::sys::weak::syscall; -#[cfg(not(any(all(target_os = "linux", target_env = "gnu"), target_os = "hurd")))] -use libc::sendfile as sendfile64; -#[cfg(any(all(target_os = "linux", target_env = "gnu"), target_os = "hurd"))] -use libc::sendfile64; -use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV}; #[cfg(test)] mod tests; @@ -404,6 +406,30 @@ impl CopyWrite for &UnixStream { } } +impl CopyRead for PipeReader { + fn properties(&self) -> CopyParams { + CopyParams(FdMeta::Pipe, Some(self.as_raw_fd())) + } +} + +impl CopyRead for &PipeReader { + fn properties(&self) -> CopyParams { + CopyParams(FdMeta::Pipe, Some(self.as_raw_fd())) + } +} + +impl CopyWrite for PipeWriter { + fn properties(&self) -> CopyParams { + CopyParams(FdMeta::Pipe, Some(self.as_raw_fd())) + } +} + +impl CopyWrite for &PipeWriter { + fn properties(&self) -> CopyParams { + CopyParams(FdMeta::Pipe, Some(self.as_raw_fd())) + } +} + impl CopyWrite for ChildStdin { fn properties(&self) -> CopyParams { CopyParams(FdMeta::Pipe, Some(self.as_raw_fd())) @@ -560,6 +586,12 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> // We store the availability in a global to avoid unnecessary syscalls static HAS_COPY_FILE_RANGE: AtomicU8 = AtomicU8::new(NOT_PROBED); + let mut have_probed = match HAS_COPY_FILE_RANGE.load(Ordering::Relaxed) { + NOT_PROBED => false, + UNAVAILABLE => return CopyResult::Fallback(0), + _ => true, + }; + syscall! { fn copy_file_range( fd_in: libc::c_int, @@ -571,25 +603,22 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> ) -> libc::ssize_t } - match HAS_COPY_FILE_RANGE.load(Ordering::Relaxed) { - NOT_PROBED => { - // EPERM can indicate seccomp filters or an immutable file. - // To distinguish these cases we probe with invalid file descriptors which should result in EBADF if the syscall is supported - // and some other error (ENOSYS or EPERM) if it's not available - let result = unsafe { - cvt(copy_file_range(INVALID_FD, ptr::null_mut(), INVALID_FD, ptr::null_mut(), 1, 0)) - }; - - if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(EBADF))) { - HAS_COPY_FILE_RANGE.store(AVAILABLE, Ordering::Relaxed); - } else { - HAS_COPY_FILE_RANGE.store(UNAVAILABLE, Ordering::Relaxed); - return CopyResult::Fallback(0); - } + fn probe_copy_file_range_support() -> u8 { + // In some cases, we cannot determine availability from the first + // `copy_file_range` call. In this case, we probe with an invalid file + // descriptor so that the results are easily interpretable. + match unsafe { + cvt(copy_file_range(INVALID_FD, ptr::null_mut(), INVALID_FD, ptr::null_mut(), 1, 0)) + .map_err(|e| e.raw_os_error()) + } { + Err(Some(EPERM | ENOSYS)) => UNAVAILABLE, + Err(Some(EBADF)) => AVAILABLE, + Ok(_) => panic!("unexpected copy_file_range probe success"), + // Treat other errors as the syscall + // being unavailable. + Err(_) => UNAVAILABLE, } - UNAVAILABLE => return CopyResult::Fallback(0), - _ => {} - }; + } let mut written = 0u64; while written < max_len { @@ -604,6 +633,11 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> cvt(copy_file_range(reader, ptr::null_mut(), writer, ptr::null_mut(), bytes_to_copy, 0)) }; + if !have_probed && copy_result.is_ok() { + have_probed = true; + HAS_COPY_FILE_RANGE.store(AVAILABLE, Ordering::Relaxed); + } + match copy_result { Ok(0) if written == 0 => { // fallback to work around several kernel bugs where copy_file_range will fail to @@ -619,7 +653,28 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> return match err.raw_os_error() { // when file offset + max_length > u64::MAX Some(EOVERFLOW) => CopyResult::Fallback(written), - Some(ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF) if written == 0 => { + Some(raw_os_error @ (ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF)) + if written == 0 => + { + if !have_probed { + let available = if matches!(raw_os_error, ENOSYS | EOPNOTSUPP | EPERM) { + // EPERM can indicate seccomp filters or an + // immutable file. To distinguish these + // cases we probe with invalid file + // descriptors which should result in EBADF + // if the syscall is supported and EPERM or + // ENOSYS if it's not available. + // + // For EOPNOTSUPP, see below. In the case of + // ENOSYS, we try to cover for faulty FUSE + // drivers. + probe_copy_file_range_support() + } else { + AVAILABLE + }; + HAS_COPY_FILE_RANGE.store(available, Ordering::Relaxed); + } + // Try fallback io::copy if either: // - Kernel version is < 4.5 (ENOSYS¹) // - Files are mounted on different fs (EXDEV) diff --git a/library/std/src/sys/pal/unix/kernel_copy/tests.rs b/library/std/src/sys/pal/unix/kernel_copy/tests.rs index a524270e3fb85..1350d743ff6f3 100644 --- a/library/std/src/sys/pal/unix/kernel_copy/tests.rs +++ b/library/std/src/sys/pal/unix/kernel_copy/tests.rs @@ -1,8 +1,6 @@ use crate::fs::OpenOptions; use crate::io; -use crate::io::Result; -use crate::io::SeekFrom; -use crate::io::{BufRead, Read, Seek, Write}; +use crate::io::{BufRead, Read, Result, Seek, SeekFrom, Write}; use crate::os::unix::io::AsRawFd; use crate::sys_common::io::test::tmpdir; diff --git a/library/std/src/sys/pal/unix/linux/mod.rs b/library/std/src/sys/pal/unix/linux/mod.rs new file mode 100644 index 0000000000000..88aa1e3deccf8 --- /dev/null +++ b/library/std/src/sys/pal/unix/linux/mod.rs @@ -0,0 +1 @@ +pub mod pidfd; diff --git a/library/std/src/sys/pal/unix/linux/pidfd.rs b/library/std/src/sys/pal/unix/linux/pidfd.rs new file mode 100644 index 0000000000000..7474f80e94f9d --- /dev/null +++ b/library/std/src/sys/pal/unix/linux/pidfd.rs @@ -0,0 +1,76 @@ +use crate::io; +use crate::os::fd::{AsRawFd, FromRawFd, RawFd}; +use crate::sys::cvt; +use crate::sys::pal::unix::fd::FileDesc; +use crate::sys::process::ExitStatus; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +#[cfg(test)] +mod tests; + +#[derive(Debug)] +pub(crate) struct PidFd(FileDesc); + +impl PidFd { + pub fn kill(&self) -> io::Result<()> { + return cvt(unsafe { + libc::syscall( + libc::SYS_pidfd_send_signal, + self.0.as_raw_fd(), + libc::SIGKILL, + crate::ptr::null::<()>(), + 0, + ) + }) + .map(drop); + } + + pub fn wait(&self) -> io::Result { + let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; + cvt(unsafe { + libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, libc::WEXITED) + })?; + return Ok(ExitStatus::from_waitid_siginfo(siginfo)); + } + + pub fn try_wait(&self) -> io::Result> { + let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; + + cvt(unsafe { + libc::waitid( + libc::P_PIDFD, + self.0.as_raw_fd() as u32, + &mut siginfo, + libc::WEXITED | libc::WNOHANG, + ) + })?; + if unsafe { siginfo.si_pid() } == 0 { + return Ok(None); + } + return Ok(Some(ExitStatus::from_waitid_siginfo(siginfo))); + } +} + +impl AsInner for PidFd { + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl IntoInner for PidFd { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for PidFd { + fn from_inner(inner: FileDesc) -> Self { + Self(inner) + } +} + +impl FromRawFd for PidFd { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + Self(FileDesc::from_raw_fd(fd)) + } +} diff --git a/library/std/src/sys/pal/unix/linux/pidfd/tests.rs b/library/std/src/sys/pal/unix/linux/pidfd/tests.rs new file mode 100644 index 0000000000000..fb928c76fbd04 --- /dev/null +++ b/library/std/src/sys/pal/unix/linux/pidfd/tests.rs @@ -0,0 +1,99 @@ +use crate::assert_matches::assert_matches; +use crate::os::fd::{AsRawFd, RawFd}; +use crate::os::linux::process::{ChildExt, CommandExt as _}; +use crate::os::unix::process::{CommandExt as _, ExitStatusExt}; +use crate::process::Command; + +#[test] +fn test_command_pidfd() { + let pidfd_open_available = probe_pidfd_support(); + + // always exercise creation attempts + let mut child = Command::new("false").create_pidfd(true).spawn().unwrap(); + + // but only check if we know that the kernel supports pidfds. + // We don't assert the precise value, since the standard library + // might have opened other file descriptors before our code runs. + if pidfd_open_available { + assert!(child.pidfd().is_ok()); + } + if let Ok(pidfd) = child.pidfd() { + let flags = super::cvt(unsafe { libc::fcntl(pidfd.as_raw_fd(), libc::F_GETFD) }).unwrap(); + assert!(flags & libc::FD_CLOEXEC != 0); + } + assert!(child.id() > 0 && child.id() < -1i32 as u32); + let status = child.wait().expect("error waiting on pidfd"); + assert_eq!(status.code(), Some(1)); + + let mut child = Command::new("sleep").arg("1000").create_pidfd(true).spawn().unwrap(); + assert_matches!(child.try_wait(), Ok(None)); + child.kill().expect("failed to kill child"); + let status = child.wait().expect("error waiting on pidfd"); + assert_eq!(status.signal(), Some(libc::SIGKILL)); + + let _ = Command::new("echo") + .create_pidfd(false) + .spawn() + .unwrap() + .pidfd() + .expect_err("pidfd should not have been created when create_pid(false) is set"); + + let _ = Command::new("echo") + .spawn() + .unwrap() + .pidfd() + .expect_err("pidfd should not have been created"); + + // exercise the fork/exec path since the earlier attempts may have used pidfd_spawnp() + let mut child = + unsafe { Command::new("false").pre_exec(|| Ok(())) }.create_pidfd(true).spawn().unwrap(); + + assert!(child.id() > 0 && child.id() < -1i32 as u32); + + if pidfd_open_available { + assert!(child.pidfd().is_ok()) + } + child.wait().expect("error waiting on child"); +} + +#[test] +fn test_pidfd() { + if !probe_pidfd_support() { + return; + } + + let child = Command::new("sleep") + .arg("1000") + .create_pidfd(true) + .spawn() + .expect("executing 'sleep' failed"); + + let fd = child.into_pidfd().unwrap(); + + assert_matches!(fd.try_wait(), Ok(None)); + fd.kill().expect("kill failed"); + fd.kill().expect("sending kill twice failed"); + let status = fd.wait().expect("1st wait failed"); + assert_eq!(status.signal(), Some(libc::SIGKILL)); + + // Trying to wait again for a reaped child is safe since there's no pid-recycling race. + // But doing so will return an error. + let res = fd.wait(); + assert_matches!(res, Err(e) if e.raw_os_error() == Some(libc::ECHILD)); + + // Ditto for additional attempts to kill an already-dead child. + let res = fd.kill(); + assert_matches!(res, Err(e) if e.raw_os_error() == Some(libc::ESRCH)); +} + +fn probe_pidfd_support() -> bool { + // pidfds require the pidfd_open syscall + let our_pid = crate::process::id(); + let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) }; + if pidfd >= 0 { + unsafe { libc::close(pidfd as RawFd) }; + true + } else { + false + } +} diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 735ed96bc7b16..b62129f4cdd26 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -1,15 +1,13 @@ #![allow(missing_docs, nonstandard_style)] -use crate::io::ErrorKind; - pub use self::rand::hashmap_random_keys; +use crate::io::ErrorKind; #[cfg(not(target_os = "espidf"))] #[macro_use] pub mod weak; pub mod alloc; -pub mod android; pub mod args; pub mod env; pub mod fd; @@ -20,6 +18,8 @@ pub mod io; pub mod kernel_copy; #[cfg(target_os = "l4re")] mod l4re; +#[cfg(target_os = "linux")] +pub mod linux; #[cfg(not(target_os = "l4re"))] pub mod net; #[cfg(target_os = "l4re")] @@ -31,8 +31,6 @@ pub mod rand; pub mod stack_overflow; pub mod stdio; pub mod thread; -pub mod thread_local_dtor; -pub mod thread_local_key; pub mod thread_parking; pub mod time; @@ -86,11 +84,12 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_vendor = "apple", )))] 'poll: { - use crate::sys::os::errno; #[cfg(not(all(target_os = "linux", target_env = "gnu")))] use libc::open as open64; #[cfg(all(target_os = "linux", target_env = "gnu"))] use libc::open64; + + use crate::sys::os::errno; let pfds: &mut [_] = &mut [ libc::pollfd { fd: 0, events: 0, revents: 0 }, libc::pollfd { fd: 1, events: 0, revents: 0 }, @@ -140,11 +139,12 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "vita", )))] { - use crate::sys::os::errno; #[cfg(not(all(target_os = "linux", target_env = "gnu")))] use libc::open as open64; #[cfg(all(target_os = "linux", target_env = "gnu"))] use libc::open64; + + use crate::sys::os::errno; for fd in 0..3 { if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF { if open64(c"/dev/null".as_ptr().cast(), libc::O_RDWR, 0) == -1 { @@ -164,6 +164,8 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "emscripten", target_os = "fuchsia", target_os = "horizon", + target_os = "vxworks", + target_os = "vita", // Unikraft's `signal` implementation is currently broken: // https://github.com/unikraft/lib-musl/issues/57 target_vendor = "unikraft", @@ -209,6 +211,8 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "emscripten", target_os = "fuchsia", target_os = "horizon", + target_os = "vxworks", + target_os = "vita", )))] static ON_BROKEN_PIPE_FLAG_USED: crate::sync::atomic::AtomicBool = crate::sync::atomic::AtomicBool::new(false); @@ -218,6 +222,8 @@ static ON_BROKEN_PIPE_FLAG_USED: crate::sync::atomic::AtomicBool = target_os = "emscripten", target_os = "fuchsia", target_os = "horizon", + target_os = "vxworks", + target_os = "vita", )))] pub(crate) fn on_broken_pipe_flag_used() -> bool { ON_BROKEN_PIPE_FLAG_USED.load(crate::sync::atomic::Ordering::Relaxed) @@ -229,10 +235,7 @@ pub unsafe fn cleanup() { stack_overflow::cleanup(); } -#[cfg(target_os = "android")] -pub use crate::sys::android::signal; #[allow(unused_imports)] -#[cfg(not(target_os = "android"))] pub use libc::signal; #[inline] @@ -305,10 +308,13 @@ macro_rules! impl_is_minus_one { impl_is_minus_one! { i8 i16 i32 i64 isize } +/// Converts native return values to Result using the *-1 means error is in `errno`* convention. +/// Non-error values are `Ok`-wrapped. pub fn cvt(t: T) -> crate::io::Result { if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } } +/// `-1` → look at `errno` → retry on `EINTR`. Otherwise `Ok()`-wrap the closure return value. pub fn cvt_r(mut f: F) -> crate::io::Result where T: IsMinusOne, @@ -323,6 +329,7 @@ where } #[allow(dead_code)] // Not used on all platforms. +/// Zero means `Ok()`, all other values are treated as raw OS errors. Does not look at `errno`. pub fn cvt_nz(error: libc::c_int) -> crate::io::Result<()> { if error == 0 { Ok(()) } else { Err(crate::io::Error::from_raw_os_error(error)) } } diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index b8dc1538a6378..bc0e3f4eeeac8 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/pal/unix/net.rs @@ -1,17 +1,15 @@ -use crate::cmp; +use libc::{c_int, c_void, size_t, sockaddr, socklen_t, MSG_PEEK}; + use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::mem; use crate::net::{Shutdown, SocketAddr}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::str; use crate::sys::fd::FileDesc; use crate::sys::pal::unix::IsMinusOne; use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; - -use libc::{c_int, c_void, size_t, sockaddr, socklen_t, MSG_PEEK}; +use crate::{cmp, mem}; cfg_if::cfg_if! { if #[cfg(target_vendor = "apple")] { @@ -47,7 +45,9 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> { #[cfg(not(target_os = "espidf"))] let detail = unsafe { - str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap().to_owned() + // We can't always expect a UTF-8 environment. When we don't get that luxury, + // it's better to give a low-quality error message than none at all. + CStr::from_ptr(libc::gai_strerror(err)).to_string_lossy() }; #[cfg(target_os = "espidf")] @@ -213,16 +213,25 @@ impl Socket { } 0 => {} _ => { - // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look - // for POLLHUP rather than read readiness - if pollfd.revents & libc::POLLHUP != 0 { - let e = self.take_error()?.unwrap_or_else(|| { - io::const_io_error!( - io::ErrorKind::Uncategorized, - "no error set after POLLHUP", - ) - }); - return Err(e); + if cfg!(target_os = "vxworks") { + // VxWorks poll does not return POLLHUP or POLLERR in revents. Check if the + // connnection actually succeeded and return ok only when the socket is + // ready and no errors were found. + if let Some(e) = self.take_error()? { + return Err(e); + } + } else { + // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look + // for POLLHUP or POLLERR rather than read readiness + if pollfd.revents & (libc::POLLHUP | libc::POLLERR) != 0 { + let e = self.take_error()?.unwrap_or_else(|| { + io::const_io_error!( + io::ErrorKind::Uncategorized, + "no error set after POLLHUP", + ) + }); + return Err(e); + } } return Ok(()); diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 8afc49f52274c..a785b97ac8dc5 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -5,29 +5,20 @@ #[cfg(test)] mod tests; -use crate::os::unix::prelude::*; +use core::slice::memchr; + +use libc::{c_char, c_int, c_void}; use crate::error::Error as StdError; use crate::ffi::{CStr, CString, OsStr, OsString}; -use crate::fmt; -use crate::io; -use crate::iter; -use crate::mem; +use crate::os::unix::prelude::*; use crate::path::{self, PathBuf}; -use crate::ptr; -use crate::slice; -use crate::str; use crate::sync::{PoisonError, RwLock}; use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; -use crate::sys::cvt; -use crate::sys::fd; -use crate::vec; -use core::slice::memchr; - #[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] use crate::sys::weak::weak; - -use libc::{c_char, c_int, c_void}; +use crate::sys::{cvt, fd}; +use crate::{fmt, io, iter, mem, ptr, slice, str, vec}; const TMPBUF_SZ: usize = 128; @@ -248,13 +239,12 @@ impl StdError for JoinPathsError { #[cfg(target_os = "aix")] pub fn current_exe() -> io::Result { - use crate::io::ErrorKind; - #[cfg(test)] use realstd::env; #[cfg(not(test))] use crate::env; + use crate::io::ErrorKind; let exe_path = env::args().next().ok_or(io::const_io_error!( ErrorKind::NotFound, @@ -462,21 +452,21 @@ pub fn current_exe() -> io::Result { #[cfg(target_os = "haiku")] pub fn current_exe() -> io::Result { + let mut name = vec![0; libc::PATH_MAX as usize]; unsafe { - let mut info: mem::MaybeUninit = mem::MaybeUninit::uninit(); - let mut cookie: i32 = 0; - // the executable can be found at team id 0 - let result = libc::_get_next_image_info( - 0, - &mut cookie, - info.as_mut_ptr(), - mem::size_of::(), + let result = libc::find_path( + crate::ptr::null_mut(), + libc::path_base_directory::B_FIND_PATH_IMAGE_PATH, + crate::ptr::null_mut(), + name.as_mut_ptr(), + name.len(), ); - if result != 0 { + if result != libc::B_OK { use crate::io::ErrorKind; Err(io::const_io_error!(ErrorKind::Uncategorized, "Error getting executable path")) } else { - let name = CStr::from_ptr((*info.as_ptr()).name.as_ptr()).to_bytes(); + // find_path adds the null terminator. + let name = CStr::from_ptr(name.as_ptr()).to_bytes(); Ok(PathBuf::from(OsStr::from_bytes(name))) } } @@ -513,13 +503,12 @@ pub fn current_exe() -> io::Result { #[cfg(target_os = "fuchsia")] pub fn current_exe() -> io::Result { - use crate::io::ErrorKind; - #[cfg(test)] use realstd::env; #[cfg(not(test))] use crate::env; + use crate::io::ErrorKind; let exe_path = env::args().next().ok_or(io::const_io_error!( ErrorKind::Uncategorized, @@ -675,19 +664,19 @@ pub fn getenv(k: &OsStr) -> Option { .flatten() } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { run_with_cstr(k.as_bytes(), &|k| { run_with_cstr(v.as_bytes(), &|v| { let _guard = ENV_LOCK.write(); - cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop) + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) }) }) } -pub fn unsetenv(n: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { run_with_cstr(n.as_bytes(), &|nbuf| { let _guard = ENV_LOCK.write(); - cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) + cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) }) } @@ -738,17 +727,17 @@ pub fn home_dir() -> Option { n => n as usize, }; let mut buf = Vec::with_capacity(amt); - let mut passwd: libc::passwd = mem::zeroed(); + let mut p = mem::MaybeUninit::::uninit(); let mut result = ptr::null_mut(); match libc::getpwuid_r( libc::getuid(), - &mut passwd, + p.as_mut_ptr(), buf.as_mut_ptr(), buf.capacity(), &mut result, ) { 0 if !result.is_null() => { - let ptr = passwd.pw_dir as *const _; + let ptr = (*result).pw_dir as *const _; let bytes = CStr::from_ptr(ptr).to_bytes().to_vec(); Some(OsStringExt::from_vec(bytes)) } @@ -758,6 +747,7 @@ pub fn home_dir() -> Option { } pub fn exit(code: i32) -> ! { + crate::sys::exit_guard::unique_thread_exit(); unsafe { libc::exit(code as c_int) } } diff --git a/library/std/src/sys/pal/unix/pipe.rs b/library/std/src/sys/pal/unix/pipe.rs index 33db24e77e4da..f0ebc767badad 100644 --- a/library/std/src/sys/pal/unix/pipe.rs +++ b/library/std/src/sys/pal/unix/pipe.rs @@ -9,6 +9,7 @@ use crate::sys_common::{FromInner, IntoInner}; // Anonymous pipes //////////////////////////////////////////////////////////////////////////////// +#[derive(Debug)] pub struct AnonPipe(FileDesc); pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { @@ -46,6 +47,12 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { } impl AnonPipe { + #[allow(dead_code)] + // FIXME: This function seems legitimately unused. + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(Self) + } + pub fn read(&self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } @@ -79,6 +86,12 @@ impl AnonPipe { pub fn is_write_vectored(&self) -> bool { self.0.is_write_vectored() } + + #[allow(dead_code)] + // FIXME: This function seems legitimately unused. + pub fn as_file_desc(&self) -> &FileDesc { + &self.0 + } } impl IntoInner for AnonPipe { diff --git a/library/std/src/sys/pal/unix/process/process_common.rs b/library/std/src/sys/pal/unix/process/process_common.rs index f615e8086dccf..fec825054195a 100644 --- a/library/std/src/sys/pal/unix/process/process_common.rs +++ b/library/std/src/sys/pal/unix/process/process_common.rs @@ -1,24 +1,20 @@ #[cfg(all(test, not(target_os = "emscripten")))] mod tests; -use crate::os::unix::prelude::*; +use libc::{c_char, c_int, gid_t, pid_t, uid_t, EXIT_FAILURE, EXIT_SUCCESS}; use crate::collections::BTreeMap; use crate::ffi::{CStr, CString, OsStr, OsString}; -use crate::fmt; -use crate::io; +use crate::os::unix::prelude::*; use crate::path::Path; -use crate::ptr; use crate::sys::fd::FileDesc; use crate::sys::fs::File; +#[cfg(not(target_os = "fuchsia"))] +use crate::sys::fs::OpenOptions; use crate::sys::pipe::{self, AnonPipe}; use crate::sys_common::process::{CommandEnv, CommandEnvs}; use crate::sys_common::{FromInner, IntoInner}; - -#[cfg(not(target_os = "fuchsia"))] -use crate::sys::fs::OpenOptions; - -use libc::{c_char, c_int, gid_t, pid_t, uid_t, EXIT_FAILURE, EXIT_SUCCESS}; +use crate::{fmt, io, ptr}; cfg_if::cfg_if! { if #[cfg(target_os = "fuchsia")] { @@ -128,6 +124,7 @@ pub struct StdioPipes { // passed to do_exec() with configuration of what the child stdio should look // like +#[cfg_attr(target_os = "vita", allow(dead_code))] pub struct ChildPipes { pub stdin: ChildStdio, pub stdout: ChildStdio, diff --git a/library/std/src/sys/pal/unix/process/process_common/tests.rs b/library/std/src/sys/pal/unix/process/process_common/tests.rs index 823b4a5633629..e5c8dd6e341e1 100644 --- a/library/std/src/sys/pal/unix/process/process_common/tests.rs +++ b/library/std/src/sys/pal/unix/process/process_common/tests.rs @@ -1,9 +1,7 @@ use super::*; - use crate::ffi::OsStr; -use crate::mem; -use crate::ptr; use crate::sys::{cvt, cvt_nz}; +use crate::{mem, ptr}; macro_rules! t { ($e:expr) => { diff --git a/library/std/src/sys/pal/unix/process/process_fuchsia.rs b/library/std/src/sys/pal/unix/process/process_fuchsia.rs index 23c2be6adf9ee..f3d5fdec4c291 100644 --- a/library/std/src/sys/pal/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/pal/unix/process/process_fuchsia.rs @@ -1,13 +1,9 @@ -use crate::fmt; -use crate::io; -use crate::mem; -use crate::num::NonZero; -use crate::ptr; +use libc::{c_int, size_t}; +use crate::num::NonZero; use crate::sys::process::process_common::*; use crate::sys::process::zircon::{zx_handle_t, Handle}; - -use libc::{c_int, size_t}; +use crate::{fmt, io, mem, ptr}; //////////////////////////////////////////////////////////////////////////////// // Command diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index e2fca8c7e63dc..5552e9ac97753 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -1,22 +1,7 @@ -use crate::fmt; -use crate::io::{self, Error, ErrorKind}; -use crate::mem; -use crate::num::NonZero; -use crate::sys; -use crate::sys::cvt; -use crate::sys::process::process_common::*; - -#[cfg(target_os = "linux")] -use crate::os::linux::process::PidFd; -#[cfg(target_os = "linux")] -use crate::os::unix::io::AsRawFd; - #[cfg(target_os = "vxworks")] use libc::RTP_ID as pid_t; - #[cfg(not(target_os = "vxworks"))] use libc::{c_int, pid_t}; - #[cfg(not(any( target_os = "vxworks", target_os = "l4re", @@ -25,6 +10,14 @@ use libc::{c_int, pid_t}; )))] use libc::{gid_t, uid_t}; +use crate::io::{self, Error, ErrorKind}; +use crate::num::NonZero; +use crate::sys::cvt; +#[cfg(target_os = "linux")] +use crate::sys::pal::unix::linux::pidfd::PidFd; +use crate::sys::process::process_common::*; +use crate::{fmt, mem, sys}; + cfg_if::cfg_if! { if #[cfg(all(target_os = "nto", target_env = "nto71"))] { use crate::thread; @@ -448,6 +441,9 @@ impl Command { stdio: &ChildPipes, envp: Option<&CStringArray>, ) -> io::Result> { + #[cfg(target_os = "linux")] + use core::sync::atomic::{AtomicU8, Ordering}; + use crate::mem::MaybeUninit; use crate::sys::weak::weak; use crate::sys::{self, cvt_nz, on_broken_pipe_flag_used}; @@ -457,11 +453,74 @@ impl Command { || (self.env_saw_path() && !self.program_is_path()) || !self.get_closures().is_empty() || self.get_groups().is_some() - || self.get_create_pidfd() { return Ok(None); } + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + weak! { + fn pidfd_spawnp( + *mut libc::c_int, + *const libc::c_char, + *const libc::posix_spawn_file_actions_t, + *const libc::posix_spawnattr_t, + *const *mut libc::c_char, + *const *mut libc::c_char + ) -> libc::c_int + } + + weak! { fn pidfd_getpid(libc::c_int) -> libc::c_int } + + static PIDFD_SUPPORTED: AtomicU8 = AtomicU8::new(0); + const UNKNOWN: u8 = 0; + const SPAWN: u8 = 1; + // Obtaining a pidfd via the fork+exec path might work + const FORK_EXEC: u8 = 2; + // Neither pidfd_spawn nor fork/exec will get us a pidfd. + // Instead we'll just posix_spawn if the other preconditions are met. + const NO: u8 = 3; + + if self.get_create_pidfd() { + let mut support = PIDFD_SUPPORTED.load(Ordering::Relaxed); + if support == FORK_EXEC { + return Ok(None); + } + if support == UNKNOWN { + support = NO; + let our_pid = crate::process::id(); + let pidfd = cvt(unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) } as c_int); + match pidfd { + Ok(pidfd) => { + support = FORK_EXEC; + if let Some(Ok(pid)) = pidfd_getpid.get().map(|f| cvt(unsafe { f(pidfd) } as i32)) { + if pidfd_spawnp.get().is_some() && pid as u32 == our_pid { + support = SPAWN + } + } + unsafe { libc::close(pidfd) }; + } + Err(e) if e.raw_os_error() == Some(libc::EMFILE) => { + // We're temporarily(?) out of file descriptors. In this case obtaining a pidfd would also fail + // Don't update the support flag so we can probe again later. + return Err(e) + } + _ => {} + } + PIDFD_SUPPORTED.store(support, Ordering::Relaxed); + if support == FORK_EXEC { + return Ok(None); + } + } + core::assert_matches::debug_assert_matches!(support, SPAWN | NO); + } + } else { + if self.get_create_pidfd() { + unreachable!("only implemented on linux") + } + } + } + // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly. #[cfg(all(target_os = "linux", target_env = "gnu"))] { @@ -545,9 +604,6 @@ impl Command { let pgroup = self.get_pgroup(); - // Safety: -1 indicates we don't have a pidfd. - let mut p = unsafe { Process::new(0, -1) }; - struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit); impl Drop for PosixSpawnFileActions<'_> { @@ -642,6 +698,47 @@ impl Command { #[cfg(target_os = "nto")] let spawn_fn = retrying_libc_posix_spawnp; + #[cfg(target_os = "linux")] + if self.get_create_pidfd() && PIDFD_SUPPORTED.load(Ordering::Relaxed) == SPAWN { + let mut pidfd: libc::c_int = -1; + let spawn_res = pidfd_spawnp.get().unwrap()( + &mut pidfd, + self.get_program_cstr().as_ptr(), + file_actions.0.as_ptr(), + attrs.0.as_ptr(), + self.get_argv().as_ptr() as *const _, + envp as *const _, + ); + + let spawn_res = cvt_nz(spawn_res); + if let Err(ref e) = spawn_res + && e.raw_os_error() == Some(libc::ENOSYS) + { + PIDFD_SUPPORTED.store(FORK_EXEC, Ordering::Relaxed); + return Ok(None); + } + spawn_res?; + + let pid = match cvt(pidfd_getpid.get().unwrap()(pidfd)) { + Ok(pid) => pid, + Err(e) => { + // The child has been spawned and we are holding its pidfd. + // But we cannot obtain its pid even though pidfd_getpid support was verified earlier. + // This might happen if libc can't open procfs because the file descriptor limit has been reached. + libc::close(pidfd); + return Err(Error::new( + e.kind(), + "pidfd_spawnp succeeded but the child's PID could not be obtained", + )); + } + }; + + return Ok(Some(Process::new(pid, pidfd))); + } + + // Safety: -1 indicates we don't have a pidfd. + let mut p = Process::new(0, -1); + let spawn_res = spawn_fn( &mut p.pid, self.get_program_cstr().as_ptr(), @@ -661,10 +758,11 @@ impl Command { #[cfg(target_os = "linux")] fn send_pidfd(&self, sock: &crate::sys::net::Socket) { + use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; + use crate::io::IoSlice; use crate::os::fd::RawFd; use crate::sys::cvt_r; - use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; unsafe { let child_pid = libc::getpid(); @@ -718,11 +816,11 @@ impl Command { #[cfg(target_os = "linux")] fn recv_pidfd(&self, sock: &crate::sys::net::Socket) -> pid_t { + use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; + use crate::io::IoSliceMut; use crate::sys::cvt_r; - use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; - unsafe { const SCM_MSG_LEN: usize = mem::size_of::<[c_int; 1]>(); @@ -788,6 +886,12 @@ pub struct Process { impl Process { #[cfg(target_os = "linux")] + /// # Safety + /// + /// `pidfd` must either be -1 (representing no file descriptor) or a valid, exclusively owned file + /// descriptor (See [I/O Safety]). + /// + /// [I/O Safety]: crate::io#io-safety unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self { use crate::os::unix::io::FromRawFd; use crate::sys_common::FromInner; @@ -815,16 +919,7 @@ impl Process { #[cfg(target_os = "linux")] if let Some(pid_fd) = self.pidfd.as_ref() { // pidfd_send_signal predates pidfd_open. so if we were able to get an fd then sending signals will work too - return cvt(unsafe { - libc::syscall( - libc::SYS_pidfd_send_signal, - pid_fd.as_raw_fd(), - libc::SIGKILL, - crate::ptr::null::<()>(), - 0, - ) - }) - .map(drop); + return pid_fd.kill(); } cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) } @@ -836,12 +931,7 @@ impl Process { } #[cfg(target_os = "linux")] if let Some(pid_fd) = self.pidfd.as_ref() { - let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; - - cvt_r(|| unsafe { - libc::waitid(libc::P_PIDFD, pid_fd.as_raw_fd() as u32, &mut siginfo, libc::WEXITED) - })?; - let status = ExitStatus::from_waitid_siginfo(siginfo); + let status = pid_fd.wait()?; self.status = Some(status); return Ok(status); } @@ -857,22 +947,11 @@ impl Process { } #[cfg(target_os = "linux")] if let Some(pid_fd) = self.pidfd.as_ref() { - let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; - - cvt(unsafe { - libc::waitid( - libc::P_PIDFD, - pid_fd.as_raw_fd() as u32, - &mut siginfo, - libc::WEXITED | libc::WNOHANG, - ) - })?; - if unsafe { siginfo.si_pid() } == 0 { - return Ok(None); + let status = pid_fd.try_wait()?; + if let Some(status) = status { + self.status = Some(status) } - let status = ExitStatus::from_waitid_siginfo(siginfo); - self.status = Some(status); - return Ok(Some(status)); + return Ok(status); } let mut status = 0 as c_int; let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?; @@ -965,7 +1044,7 @@ impl From for ExitStatus { } } -/// Convert a signal number to a readable, searchable name. +/// Converts a signal number to a readable, searchable name. /// /// This string should be displayed right after the signal number. /// If a signal is unrecognized, it returns the empty string, so that @@ -1053,6 +1132,10 @@ fn signal_string(signal: i32) -> &'static str { libc::SIGINFO => " (SIGINFO)", #[cfg(target_os = "hurd")] libc::SIGLOST => " (SIGLOST)", + #[cfg(target_os = "freebsd")] + libc::SIGTHR => " (SIGTHR)", + #[cfg(target_os = "freebsd")] + libc::SIGLIBRT => " (SIGLIBRT)", _ => "", } } @@ -1101,20 +1184,32 @@ impl ExitStatusError { } #[cfg(target_os = "linux")] -#[unstable(feature = "linux_pidfd", issue = "82971")] -impl crate::os::linux::process::ChildExt for crate::process::Child { - fn pidfd(&self) -> io::Result<&PidFd> { - self.handle - .pidfd - .as_ref() - .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created.")) - } +mod linux_child_ext { + + use crate::os::linux::process as os; + use crate::sys::pal::unix::linux::pidfd as imp; + use crate::sys::pal::unix::ErrorKind; + use crate::sys_common::FromInner; + use crate::{io, mem}; + + #[unstable(feature = "linux_pidfd", issue = "82971")] + impl crate::os::linux::process::ChildExt for crate::process::Child { + fn pidfd(&self) -> io::Result<&os::PidFd> { + self.handle + .pidfd + .as_ref() + // SAFETY: The os type is a transparent wrapper, therefore we can transmute references + .map(|fd| unsafe { mem::transmute::<&imp::PidFd, &os::PidFd>(fd) }) + .ok_or_else(|| io::Error::new(ErrorKind::Uncategorized, "No pidfd was created.")) + } - fn take_pidfd(&mut self) -> io::Result { - self.handle - .pidfd - .take() - .ok_or_else(|| Error::new(ErrorKind::Uncategorized, "No pidfd was created.")) + fn into_pidfd(mut self) -> Result { + self.handle + .pidfd + .take() + .map(|fd| >::from_inner(fd)) + .ok_or_else(|| self) + } } } diff --git a/library/std/src/sys/pal/unix/process/process_unix/tests.rs b/library/std/src/sys/pal/unix/process/process_unix/tests.rs index 0a6c6ec19fc7e..e5e1f956bc351 100644 --- a/library/std/src/sys/pal/unix/process/process_unix/tests.rs +++ b/library/std/src/sys/pal/unix/process/process_unix/tests.rs @@ -60,57 +60,3 @@ fn test_command_fork_no_unwind() { || signal == libc::SIGSEGV ); } - -#[test] -#[cfg(target_os = "linux")] // pidfds are a linux-specific concept -fn test_command_pidfd() { - use crate::assert_matches::assert_matches; - use crate::os::fd::{AsRawFd, RawFd}; - use crate::os::linux::process::{ChildExt, CommandExt}; - use crate::process::Command; - - // pidfds require the pidfd_open syscall - let our_pid = crate::process::id(); - let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) }; - let pidfd_open_available = if pidfd >= 0 { - unsafe { libc::close(pidfd as RawFd) }; - true - } else { - false - }; - - // always exercise creation attempts - let mut child = Command::new("false").create_pidfd(true).spawn().unwrap(); - - // but only check if we know that the kernel supports pidfds. - // We don't assert the precise value, since the standard library - // might have opened other file descriptors before our code runs. - if pidfd_open_available { - assert!(child.pidfd().is_ok()); - } - if let Ok(pidfd) = child.pidfd() { - let flags = super::cvt(unsafe { libc::fcntl(pidfd.as_raw_fd(), libc::F_GETFD) }).unwrap(); - assert!(flags & libc::FD_CLOEXEC != 0); - } - let status = child.wait().expect("error waiting on pidfd"); - assert_eq!(status.code(), Some(1)); - - let mut child = Command::new("sleep").arg("1000").create_pidfd(true).spawn().unwrap(); - assert_matches!(child.try_wait(), Ok(None)); - child.kill().expect("failed to kill child"); - let status = child.wait().expect("error waiting on pidfd"); - assert_eq!(status.signal(), Some(libc::SIGKILL)); - - let _ = Command::new("echo") - .create_pidfd(false) - .spawn() - .unwrap() - .pidfd() - .expect_err("pidfd should not have been created when create_pid(false) is set"); - - let _ = Command::new("echo") - .spawn() - .unwrap() - .pidfd() - .expect_err("pidfd should not have been created"); -} diff --git a/library/std/src/sys/pal/unix/process/process_unsupported.rs b/library/std/src/sys/pal/unix/process/process_unsupported.rs index 33d359d3f8411..c58548835ff3d 100644 --- a/library/std/src/sys/pal/unix/process/process_unsupported.rs +++ b/library/std/src/sys/pal/unix/process/process_unsupported.rs @@ -1,11 +1,10 @@ -use crate::fmt; +use libc::{c_int, pid_t}; + use crate::io; use crate::num::NonZero; use crate::sys::pal::unix::unsupported::*; use crate::sys::process::process_common::*; -use libc::{c_int, pid_t}; - //////////////////////////////////////////////////////////////////////////////// // Command //////////////////////////////////////////////////////////////////////////////// diff --git a/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs b/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs index e6dfadcf4a4cf..f04036bde4922 100644 --- a/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs +++ b/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs @@ -1,12 +1,12 @@ //! Emulated wait status for non-Unix #[cfg(unix) platforms //! //! Separate module to facilitate testing against a real Unix implementation. + +use super::ExitStatusError; use crate::ffi::c_int; use crate::fmt; use crate::num::NonZero; -use super::ExitStatusError; - /// Emulated wait status for use by `process_unsupported.rs` /// /// Uses the "traditional unix" encoding. For use on platfors which are `#[cfg(unix)]` diff --git a/library/std/src/sys/pal/unix/process/process_vxworks.rs b/library/std/src/sys/pal/unix/process/process_vxworks.rs index 76179e0910d9e..6a9d8fab1d412 100644 --- a/library/std/src/sys/pal/unix/process/process_vxworks.rs +++ b/library/std/src/sys/pal/unix/process/process_vxworks.rs @@ -1,12 +1,11 @@ -use crate::fmt; -use crate::io::{self, Error, ErrorKind}; +use libc::{self, c_char, c_int, RTP_ID}; + +use crate::io::{self, ErrorKind}; use crate::num::NonZero; -use crate::sys; use crate::sys::cvt; +use crate::sys::pal::unix::thread; use crate::sys::process::process_common::*; -use crate::sys_common::thread; -use libc::RTP_ID; -use libc::{self, c_char, c_int}; +use crate::{fmt, sys}; //////////////////////////////////////////////////////////////////////////////// // Command @@ -68,7 +67,12 @@ impl Command { .as_ref() .map(|c| c.as_ptr()) .unwrap_or_else(|| *sys::os::environ() as *const _); - let stack_size = thread::min_stack(); + let stack_size = crate::cmp::max( + crate::env::var_os("RUST_MIN_STACK") + .and_then(|s| s.to_str().and_then(|s| s.parse().ok())) + .unwrap_or(thread::DEFAULT_MIN_STACK_SIZE), + libc::PTHREAD_STACK_MIN, + ); // ensure that access to the environment is synchronized let _lock = sys::os::env_read_lock(); diff --git a/library/std/src/sys/pal/unix/process/zircon.rs b/library/std/src/sys/pal/unix/process/zircon.rs index 2e596486f9c86..4035e2370a3c6 100644 --- a/library/std/src/sys/pal/unix/process/zircon.rs +++ b/library/std/src/sys/pal/unix/process/zircon.rs @@ -1,11 +1,11 @@ #![allow(non_camel_case_types, unused)] +use libc::{c_int, c_void, size_t}; + use crate::io; use crate::mem::MaybeUninit; use crate::os::raw::c_char; -use libc::{c_int, c_void, size_t}; - pub type zx_handle_t = u32; pub type zx_vaddr_t = usize; pub type zx_rights_t = u32; diff --git a/library/std/src/sys/pal/unix/rand.rs b/library/std/src/sys/pal/unix/rand.rs index e6df109a6b8f2..8a78ea8e7ccc7 100644 --- a/library/std/src/sys/pal/unix/rand.rs +++ b/library/std/src/sys/pal/unix/rand.rs @@ -24,7 +24,6 @@ pub fn hashmap_random_keys() -> (u64, u64) { mod imp { use crate::fs::File; use crate::io::Read; - #[cfg(any(target_os = "linux", target_os = "android"))] use crate::sys::weak::syscall; @@ -178,9 +177,10 @@ mod imp { #[cfg(target_vendor = "apple")] mod imp { - use crate::io; use libc::{c_int, c_void, size_t}; + use crate::io; + #[inline(always)] fn random_failure() -> ! { panic!("unexpected random generation error: {}", io::Error::last_os_error()); @@ -311,8 +311,10 @@ mod imp { #[cfg(target_os = "vxworks")] mod imp { + use core::sync::atomic::AtomicBool; + use core::sync::atomic::Ordering::Relaxed; + use crate::io; - use core::sync::atomic::{AtomicBool, Ordering::Relaxed}; pub fn fill_bytes(v: &mut [u8]) { static RNG_INIT: AtomicBool = AtomicBool::new(false); diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 26c49257ad00d..9ff44b54c41a1 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -1,10 +1,8 @@ #![cfg_attr(test, allow(dead_code))] +pub use self::imp::{cleanup, init}; use self::imp::{drop_handler, make_handler}; -pub use self::imp::cleanup; -pub use self::imp::init; - pub struct Handler { data: *mut libc::c_void, } @@ -37,23 +35,23 @@ impl Drop for Handler { target_os = "solaris" ))] mod imp { + #[cfg(not(all(target_os = "linux", target_env = "gnu")))] + use libc::{mmap as mmap64, mprotect, munmap}; + #[cfg(all(target_os = "linux", target_env = "gnu"))] + use libc::{mmap64, mprotect, munmap}; + use libc::{ + sigaction, sigaltstack, sighandler_t, MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, + PROT_NONE, PROT_READ, PROT_WRITE, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSEGV, SIG_DFL, + SS_DISABLE, + }; + use super::Handler; use crate::cell::Cell; - use crate::io; - use crate::mem; use crate::ops::Range; - use crate::ptr; use crate::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; + use crate::sync::OnceLock; use crate::sys::pal::unix::os; - use crate::thread; - - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::{mmap as mmap64, mprotect, munmap}; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::{mmap64, mprotect, munmap}; - use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSEGV, SIG_DFL}; - use libc::{sigaltstack, SS_DISABLE}; - use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; + use crate::{io, mem, ptr, thread}; // We use a TLS variable to store the address of the guard page. While TLS // variables are not guaranteed to be signal-safe, this works out in practice @@ -86,13 +84,18 @@ mod imp { // out many large systems and all implementations allow returning from a // signal handler to work. For a more detailed explanation see the // comments on #26458. + /// SIGSEGV/SIGBUS entry point + /// # Safety + /// Rust doesn't call this, it *gets called*. + #[forbid(unsafe_op_in_unsafe_fn)] unsafe extern "C" fn signal_handler( signum: libc::c_int, info: *mut libc::siginfo_t, _data: *mut libc::c_void, ) { let (start, end) = GUARD.get(); - let addr = (*info).si_addr() as usize; + // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`. + let addr = unsafe { (*info).si_addr().addr() }; // If the faulting address is within the guard page, then we print a // message saying so and abort. @@ -104,9 +107,11 @@ mod imp { rtabort!("stack overflow"); } else { // Unregister ourselves by reverting back to the default behavior. - let mut action: sigaction = mem::zeroed(); + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + let mut action: sigaction = unsafe { mem::zeroed() }; action.sa_sigaction = SIG_DFL; - sigaction(signum, &action, ptr::null_mut()); + // SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction + unsafe { sigaction(signum, &action, ptr::null_mut()) }; // See comment above for why this function returns. } @@ -116,32 +121,45 @@ mod imp { static MAIN_ALTSTACK: AtomicPtr = AtomicPtr::new(ptr::null_mut()); static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false); + /// # Safety + /// Must be called only once + #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn init() { PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); // Always write to GUARD to ensure the TLS variable is allocated. - let guard = install_main_guard().unwrap_or(0..0); + let guard = unsafe { install_main_guard().unwrap_or(0..0) }; GUARD.set((guard.start, guard.end)); - let mut action: sigaction = mem::zeroed(); + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + let mut action: sigaction = unsafe { mem::zeroed() }; for &signal in &[SIGSEGV, SIGBUS] { - sigaction(signal, ptr::null_mut(), &mut action); + // SAFETY: just fetches the current signal handler into action + unsafe { sigaction(signal, ptr::null_mut(), &mut action) }; // Configure our signal handler if one is not already set. if action.sa_sigaction == SIG_DFL { + if !NEED_ALTSTACK.load(Ordering::Relaxed) { + // haven't set up our sigaltstack yet + NEED_ALTSTACK.store(true, Ordering::Release); + let handler = unsafe { make_handler(true) }; + MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); + mem::forget(handler); + } action.sa_flags = SA_SIGINFO | SA_ONSTACK; action.sa_sigaction = signal_handler as sighandler_t; - sigaction(signal, &action, ptr::null_mut()); - NEED_ALTSTACK.store(true, Ordering::Relaxed); + // SAFETY: only overriding signals if the default is set + unsafe { sigaction(signal, &action, ptr::null_mut()) }; } } - - let handler = make_handler(true); - MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); - mem::forget(handler); } + /// # Safety + /// Must be called only once + #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn cleanup() { - drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed)); + // FIXME: I probably cause more bugs than I'm worth! + // see https://github.com/rust-lang/rust/issues/111272 + unsafe { drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed)) }; } unsafe fn get_stack() -> libc::stack_t { @@ -186,34 +204,48 @@ mod imp { libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size } } + /// # Safety + /// Mutates the alternate signal stack + #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn make_handler(main_thread: bool) -> Handler { - if !NEED_ALTSTACK.load(Ordering::Relaxed) { + if !NEED_ALTSTACK.load(Ordering::Acquire) { return Handler::null(); } if !main_thread { // Always write to GUARD to ensure the TLS variable is allocated. - let guard = current_guard().unwrap_or(0..0); + let guard = unsafe { current_guard() }.unwrap_or(0..0); GUARD.set((guard.start, guard.end)); } - let mut stack = mem::zeroed(); - sigaltstack(ptr::null(), &mut stack); + // SAFETY: assuming stack_t is zero-initializable + let mut stack = unsafe { mem::zeroed() }; + // SAFETY: reads current stack_t into stack + unsafe { sigaltstack(ptr::null(), &mut stack) }; // Configure alternate signal stack, if one is not already set. if stack.ss_flags & SS_DISABLE != 0 { - stack = get_stack(); - sigaltstack(&stack, ptr::null_mut()); + // SAFETY: We warned our caller this would happen! + unsafe { + stack = get_stack(); + sigaltstack(&stack, ptr::null_mut()); + } Handler { data: stack.ss_sp as *mut libc::c_void } } else { Handler::null() } } + /// # Safety + /// Must be called + /// - only with our handler or nullptr + /// - only when done with our altstack + /// This disables the alternate signal stack! + #[forbid(unsafe_op_in_unsafe_fn)] pub unsafe fn drop_handler(data: *mut libc::c_void) { if !data.is_null() { let sigstack_size = sigstack_size(); let page_size = PAGE_SIZE.load(Ordering::Relaxed); - let stack = libc::stack_t { + let disabling_stack = libc::stack_t { ss_sp: ptr::null_mut(), ss_flags: SS_DISABLE, // Workaround for bug in macOS implementation of sigaltstack @@ -222,10 +254,11 @@ mod imp { // both ss_sp and ss_size should be ignored in this case. ss_size: sigstack_size, }; - sigaltstack(&stack, ptr::null_mut()); - // We know from `get_stackp` that the alternate stack we installed is part of a mapping - // that started one page earlier, so walk back a page and unmap from there. - munmap(data.sub(page_size), sigstack_size + page_size); + // SAFETY: we warned the caller this disables the alternate signal stack! + unsafe { sigaltstack(&disabling_stack, ptr::null_mut()) }; + // SAFETY: We know from `get_stackp` that the alternate stack we installed is part of + // a mapping that started one page earlier, so walk back a page and unmap from there. + unsafe { munmap(data.sub(page_size), sigstack_size + page_size) }; } } @@ -306,9 +339,8 @@ mod imp { ret } - unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> { - let page_size = PAGE_SIZE.load(Ordering::Relaxed); - let stackptr = get_stack_start()?; + fn stack_start_aligned(page_size: usize) -> Option<*mut libc::c_void> { + let stackptr = unsafe { get_stack_start()? }; let stackaddr = stackptr.addr(); // Ensure stackaddr is page aligned! A parent process might @@ -325,107 +357,137 @@ mod imp { }) } + #[forbid(unsafe_op_in_unsafe_fn)] unsafe fn install_main_guard() -> Option> { let page_size = PAGE_SIZE.load(Ordering::Relaxed); - if cfg!(all(target_os = "linux", not(target_env = "musl"))) { - // Linux doesn't allocate the whole stack right away, and - // the kernel has its own stack-guard mechanism to fault - // when growing too close to an existing mapping. If we map - // our own guard, then the kernel starts enforcing a rather - // large gap above that, rendering much of the possible - // stack space useless. See #43052. - // - // Instead, we'll just note where we expect rlimit to start - // faulting, so our handler can report "stack overflow", and - // trust that the kernel's own stack guard will work. - let stackptr = get_stack_start_aligned()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - page_size..stackaddr) - } else if cfg!(all(target_os = "linux", target_env = "musl")) { - // For the main thread, the musl's pthread_attr_getstack - // returns the current stack size, rather than maximum size - // it can eventually grow to. It cannot be used to determine - // the position of kernel's stack guard. - None - } else if cfg!(target_os = "freebsd") { - // FreeBSD's stack autogrows, and optionally includes a guard page - // at the bottom. If we try to remap the bottom of the stack - // ourselves, FreeBSD's guard page moves upwards. So we'll just use - // the builtin guard page. - let stackptr = get_stack_start_aligned()?; - let guardaddr = stackptr.addr(); - // Technically the number of guard pages is tunable and controlled - // by the security.bsd.stack_guard_page sysctl. - // By default it is 1, checking once is enough since it is - // a boot time config value. - static PAGES: crate::sync::OnceLock = crate::sync::OnceLock::new(); - - let pages = PAGES.get_or_init(|| { - use crate::sys::weak::dlsym; - dlsym!(fn sysctlbyname(*const libc::c_char, *mut libc::c_void, *mut libc::size_t, *const libc::c_void, libc::size_t) -> libc::c_int); - let mut guard: usize = 0; - let mut size = crate::mem::size_of_val(&guard); - let oid = crate::ffi::CStr::from_bytes_with_nul( - b"security.bsd.stack_guard_page\0", - ) - .unwrap(); - match sysctlbyname.get() { - Some(fcn) => { - if fcn(oid.as_ptr(), core::ptr::addr_of_mut!(guard) as *mut _, core::ptr::addr_of_mut!(size) as *mut _, crate::ptr::null_mut(), 0) == 0 { - guard - } else { - 1 - } - }, - _ => 1, - } - }); - Some(guardaddr..guardaddr + pages * page_size) - } else if cfg!(any(target_os = "openbsd", target_os = "netbsd")) { - // OpenBSD stack already includes a guard page, and stack is - // immutable. - // NetBSD stack includes the guard page. - // - // We'll just note where we expect rlimit to start - // faulting, so our handler can report "stack overflow", and - // trust that the kernel's own stack guard will work. - let stackptr = get_stack_start_aligned()?; - let stackaddr = stackptr.addr(); - Some(stackaddr - page_size..stackaddr) - } else { - // Reallocate the last page of the stack. - // This ensures SIGBUS will be raised on - // stack overflow. - // Systems which enforce strict PAX MPROTECT do not allow - // to mprotect() a mapping with less restrictive permissions - // than the initial mmap() used, so we mmap() here with - // read/write permissions and only then mprotect() it to - // no permissions at all. See issue #50313. - let stackptr = get_stack_start_aligned()?; - let result = mmap64( + + unsafe { + // this way someone on any unix-y OS can check that all these compile + if cfg!(all(target_os = "linux", not(target_env = "musl"))) { + install_main_guard_linux(page_size) + } else if cfg!(all(target_os = "linux", target_env = "musl")) { + install_main_guard_linux_musl(page_size) + } else if cfg!(target_os = "freebsd") { + install_main_guard_freebsd(page_size) + } else if cfg!(any(target_os = "netbsd", target_os = "openbsd")) { + install_main_guard_bsds(page_size) + } else { + install_main_guard_default(page_size) + } + } + } + + #[forbid(unsafe_op_in_unsafe_fn)] + unsafe fn install_main_guard_linux(page_size: usize) -> Option> { + // Linux doesn't allocate the whole stack right away, and + // the kernel has its own stack-guard mechanism to fault + // when growing too close to an existing mapping. If we map + // our own guard, then the kernel starts enforcing a rather + // large gap above that, rendering much of the possible + // stack space useless. See #43052. + // + // Instead, we'll just note where we expect rlimit to start + // faulting, so our handler can report "stack overflow", and + // trust that the kernel's own stack guard will work. + let stackptr = stack_start_aligned(page_size)?; + let stackaddr = stackptr.addr(); + Some(stackaddr - page_size..stackaddr) + } + + #[forbid(unsafe_op_in_unsafe_fn)] + unsafe fn install_main_guard_linux_musl(_page_size: usize) -> Option> { + // For the main thread, the musl's pthread_attr_getstack + // returns the current stack size, rather than maximum size + // it can eventually grow to. It cannot be used to determine + // the position of kernel's stack guard. + None + } + + #[forbid(unsafe_op_in_unsafe_fn)] + unsafe fn install_main_guard_freebsd(page_size: usize) -> Option> { + // FreeBSD's stack autogrows, and optionally includes a guard page + // at the bottom. If we try to remap the bottom of the stack + // ourselves, FreeBSD's guard page moves upwards. So we'll just use + // the builtin guard page. + let stackptr = stack_start_aligned(page_size)?; + let guardaddr = stackptr.addr(); + // Technically the number of guard pages is tunable and controlled + // by the security.bsd.stack_guard_page sysctl. + // By default it is 1, checking once is enough since it is + // a boot time config value. + static PAGES: OnceLock = OnceLock::new(); + + let pages = PAGES.get_or_init(|| { + use crate::sys::weak::dlsym; + dlsym!(fn sysctlbyname(*const libc::c_char, *mut libc::c_void, *mut libc::size_t, *const libc::c_void, libc::size_t) -> libc::c_int); + let mut guard: usize = 0; + let mut size = mem::size_of_val(&guard); + let oid = c"security.bsd.stack_guard_page"; + match sysctlbyname.get() { + Some(fcn) if unsafe { + fcn(oid.as_ptr(), + ptr::addr_of_mut!(guard).cast(), + ptr::addr_of_mut!(size), + ptr::null_mut(), + 0) == 0 + } => guard, + _ => 1, + } + }); + Some(guardaddr..guardaddr + pages * page_size) + } + + #[forbid(unsafe_op_in_unsafe_fn)] + unsafe fn install_main_guard_bsds(page_size: usize) -> Option> { + // OpenBSD stack already includes a guard page, and stack is + // immutable. + // NetBSD stack includes the guard page. + // + // We'll just note where we expect rlimit to start + // faulting, so our handler can report "stack overflow", and + // trust that the kernel's own stack guard will work. + let stackptr = stack_start_aligned(page_size)?; + let stackaddr = stackptr.addr(); + Some(stackaddr - page_size..stackaddr) + } + + #[forbid(unsafe_op_in_unsafe_fn)] + unsafe fn install_main_guard_default(page_size: usize) -> Option> { + // Reallocate the last page of the stack. + // This ensures SIGBUS will be raised on + // stack overflow. + // Systems which enforce strict PAX MPROTECT do not allow + // to mprotect() a mapping with less restrictive permissions + // than the initial mmap() used, so we mmap() here with + // read/write permissions and only then mprotect() it to + // no permissions at all. See issue #50313. + let stackptr = stack_start_aligned(page_size)?; + let result = unsafe { + mmap64( stackptr, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0, - ); - if result != stackptr || result == MAP_FAILED { - panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); - } + ) + }; + if result != stackptr || result == MAP_FAILED { + panic!("failed to allocate a guard page: {}", io::Error::last_os_error()); + } - let result = mprotect(stackptr, page_size, PROT_NONE); - if result != 0 { - panic!("failed to protect the guard page: {}", io::Error::last_os_error()); - } + let result = unsafe { mprotect(stackptr, page_size, PROT_NONE) }; + if result != 0 { + panic!("failed to protect the guard page: {}", io::Error::last_os_error()); + } - let guardaddr = stackptr.addr(); + let guardaddr = stackptr.addr(); - Some(guardaddr..guardaddr + page_size) - } + Some(guardaddr..guardaddr + page_size) } #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] + // FIXME: I am probably not unsafe. unsafe fn current_guard() -> Option> { let stackptr = get_stack_start()?; let stackaddr = stackptr.addr(); @@ -440,6 +502,7 @@ mod imp { target_os = "netbsd", target_os = "l4re" ))] + // FIXME: I am probably not unsafe. unsafe fn current_guard() -> Option> { let mut ret = None; let mut attr: libc::pthread_attr_t = crate::mem::zeroed(); @@ -491,6 +554,14 @@ mod imp { } } +// This is intentionally not enabled on iOS/tvOS/watchOS/visionOS, as it uses +// several symbols that might lead to rejections from the App Store, namely +// `sigaction`, `sigaltstack`, `sysctlbyname`, `mmap`, `munmap` and `mprotect`. +// +// This might be overly cautious, though it is also what Swift does (and they +// usually have fewer qualms about forwards compatibility, since the runtime +// is shipped with the OS): +// #[cfg(not(any( target_os = "linux", target_os = "freebsd", diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 853ef8736de24..44cb7b7b7ce5b 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -1,16 +1,13 @@ -use crate::cmp; use crate::ffi::CStr; -use crate::io; -use crate::mem; +use crate::mem::{self, ManuallyDrop}; use crate::num::NonZero; -use crate::ptr; -use crate::sys::{os, stack_overflow}; -use crate::time::Duration; - #[cfg(all(target_os = "linux", target_env = "gnu"))] use crate::sys::weak::dlsym; #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] use crate::sys::weak::weak; +use crate::sys::{os, stack_overflow}; +use crate::time::Duration; +use crate::{cmp, io, ptr}; #[cfg(not(any(target_os = "l4re", target_os = "vxworks", target_os = "espidf")))] pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; #[cfg(target_os = "l4re")] @@ -268,11 +265,9 @@ impl Thread { } pub fn join(self) { - unsafe { - let ret = libc::pthread_join(self.id, ptr::null_mut()); - mem::forget(self); - assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); - } + let id = self.into_id(); + let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; + assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); } pub fn id(&self) -> libc::pthread_t { @@ -280,9 +275,7 @@ impl Thread { } pub fn into_id(self) -> libc::pthread_t { - let id = self.id; - mem::forget(self); - id + ManuallyDrop::new(self).id } } @@ -462,8 +455,18 @@ pub fn available_parallelism() -> io::Result> { Ok(NonZero::new_unchecked(sinfo.cpu_count as usize)) } + } else if #[cfg(target_os = "vxworks")] { + // Note: there is also `vxCpuConfiguredGet`, closer to _SC_NPROCESSORS_CONF + // expectations than the actual cores availability. + extern "C" { + fn vxCpuEnabledGet() -> libc::cpuset_t; + } + + // always fetches a valid bitmask + let set = unsafe { vxCpuEnabledGet() }; + Ok(NonZero::new_unchecked(set.count_ones() as usize)) } else { - // FIXME: implement on vxWorks, Redox, l4re + // FIXME: implement on Redox, l4re Err(io::const_io_error!(io::ErrorKind::Unsupported, "Getting the number of hardware threads is not supported on the target platform")) } } @@ -475,14 +478,13 @@ mod cgroups { //! * cgroup v2 in non-standard mountpoints //! * paths containing control characters or spaces, since those would be escaped in procfs //! output and we don't unescape + use crate::borrow::Cow; use crate::ffi::OsString; - use crate::fs::{try_exists, File}; - use crate::io::Read; - use crate::io::{BufRead, BufReader}; + use crate::fs::{exists, File}; + use crate::io::{BufRead, BufReader, Read}; use crate::os::unix::ffi::OsStringExt; - use crate::path::Path; - use crate::path::PathBuf; + use crate::path::{Path, PathBuf}; use crate::str::from_utf8; #[derive(PartialEq)] @@ -555,7 +557,7 @@ mod cgroups { path.push("cgroup.controllers"); // skip if we're not looking at cgroup2 - if matches!(try_exists(&path), Err(_) | Ok(false)) { + if matches!(exists(&path), Err(_) | Ok(false)) { return usize::MAX; }; @@ -612,7 +614,7 @@ mod cgroups { path.push(&group_path); // skip if we guessed the mount incorrectly - if matches!(try_exists(&path), Err(_) | Ok(false)) { + if matches!(exists(&path), Err(_) | Ok(false)) { continue; } @@ -717,5 +719,14 @@ unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { #[cfg(target_os = "netbsd")] unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { - 2048 // just a guess + static STACK: crate::sync::OnceLock = crate::sync::OnceLock::new(); + + *STACK.get_or_init(|| { + let mut stack = unsafe { libc::sysconf(libc::_SC_THREAD_STACK_MIN) }; + if stack < 0 { + stack = 2048; // just a guess + } + + stack as usize + }) } diff --git a/library/std/src/sys/pal/unix/thread_local_dtor.rs b/library/std/src/sys/pal/unix/thread_local_dtor.rs deleted file mode 100644 index 75db6e112ed35..0000000000000 --- a/library/std/src/sys/pal/unix/thread_local_dtor.rs +++ /dev/null @@ -1,126 +0,0 @@ -#![cfg(target_thread_local)] -#![unstable(feature = "thread_local_internals", issue = "none")] - -//! Provides thread-local destructors without an associated "key", which -//! can be more efficient. - -// Since what appears to be glibc 2.18 this symbol has been shipped which -// GCC and clang both use to invoke destructors in thread_local globals, so -// let's do the same! -// -// Note, however, that we run on lots older linuxes, as well as cross -// compiling from a newer linux to an older linux, so we also have a -// fallback implementation to use as well. -#[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "fuchsia", - target_os = "redox", - target_os = "hurd", - target_os = "netbsd", - target_os = "dragonfly" -))] -// FIXME: The Rust compiler currently omits weakly function definitions (i.e., -// __cxa_thread_atexit_impl) and its metadata from LLVM IR. -#[no_sanitize(cfi, kcfi)] -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - use crate::mem; - use crate::sys_common::thread_local_dtor::register_dtor_fallback; - - /// This is necessary because the __cxa_thread_atexit_impl implementation - /// std links to by default may be a C or C++ implementation that was not - /// compiled using the Clang integer normalization option. - #[cfg(sanitizer_cfi_normalize_integers)] - use core::ffi::c_int; - #[cfg(not(sanitizer_cfi_normalize_integers))] - #[cfi_encoding = "i"] - #[repr(transparent)] - pub struct c_int(#[allow(dead_code)] pub libc::c_int); - - extern "C" { - #[linkage = "extern_weak"] - static __dso_handle: *mut u8; - #[linkage = "extern_weak"] - static __cxa_thread_atexit_impl: Option< - extern "C" fn( - unsafe extern "C" fn(*mut libc::c_void), - *mut libc::c_void, - *mut libc::c_void, - ) -> c_int, - >; - } - - if let Some(f) = __cxa_thread_atexit_impl { - unsafe { - f( - mem::transmute::< - unsafe extern "C" fn(*mut u8), - unsafe extern "C" fn(*mut libc::c_void), - >(dtor), - t.cast(), - core::ptr::addr_of!(__dso_handle) as *mut _, - ); - } - return; - } - register_dtor_fallback(t, dtor); -} - -// This implementation is very similar to register_dtor_fallback in -// sys_common/thread_local.rs. The main difference is that we want to hook into -// macOS's analog of the above linux function, _tlv_atexit. OSX will run the -// registered dtors before any TLS slots get freed, and when the main thread -// exits. -// -// Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The -// workaround below is to register, via _tlv_atexit, a custom DTOR list once per -// thread. thread_local dtors are pushed to the DTOR list without calling -// _tlv_atexit. -#[cfg(target_vendor = "apple")] -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - use crate::cell::{Cell, RefCell}; - use crate::ptr; - - #[thread_local] - static REGISTERED: Cell = Cell::new(false); - - #[thread_local] - static DTORS: RefCell> = RefCell::new(Vec::new()); - - if !REGISTERED.get() { - _tlv_atexit(run_dtors, ptr::null_mut()); - REGISTERED.set(true); - } - - extern "C" { - fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); - } - - match DTORS.try_borrow_mut() { - Ok(mut dtors) => dtors.push((t, dtor)), - Err(_) => rtabort!("global allocator may not use TLS"), - } - - unsafe extern "C" fn run_dtors(_: *mut u8) { - let mut list = DTORS.take(); - while !list.is_empty() { - for (ptr, dtor) in list { - dtor(ptr); - } - list = DTORS.take(); - } - } -} - -#[cfg(any( - target_os = "vxworks", - target_os = "horizon", - target_os = "emscripten", - target_os = "aix", - target_os = "freebsd", -))] -#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten) -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - use crate::sys_common::thread_local_dtor::register_dtor_fallback; - register_dtor_fallback(t, dtor); -} diff --git a/library/std/src/sys/pal/unix/thread_local_key.rs b/library/std/src/sys/pal/unix/thread_local_key.rs deleted file mode 100644 index 2b2d079ee4d01..0000000000000 --- a/library/std/src/sys/pal/unix/thread_local_key.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![allow(dead_code)] // not used on all platforms - -use crate::mem; - -pub type Key = libc::pthread_key_t; - -#[inline] -pub unsafe fn create(dtor: Option) -> Key { - let mut key = 0; - assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); - key -} - -#[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - let r = libc::pthread_setspecific(key, value as *mut _); - debug_assert_eq!(r, 0); -} - -#[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - libc::pthread_getspecific(key) as *mut u8 -} - -#[inline] -pub unsafe fn destroy(key: Key) { - let r = libc::pthread_key_delete(key); - debug_assert_eq!(r, 0); -} diff --git a/library/std/src/sys/pal/unix/thread_parking.rs b/library/std/src/sys/pal/unix/thread_parking.rs index 1366410b71edc..1da5fce3cd30f 100644 --- a/library/std/src/sys/pal/unix/thread_parking.rs +++ b/library/std/src/sys/pal/unix/thread_parking.rs @@ -2,10 +2,11 @@ // separate modules for each platform. #![cfg(target_os = "netbsd")] +use libc::{_lwp_self, c_long, clockid_t, lwpid_t, time_t, timespec, CLOCK_MONOTONIC}; + use crate::ffi::{c_int, c_void}; use crate::ptr; use crate::time::Duration; -use libc::{_lwp_self, c_long, clockid_t, lwpid_t, time_t, timespec, CLOCK_MONOTONIC}; extern "C" { fn ___lwp_park60( diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs index 4765a286e6b9e..35762f5a53b5b 100644 --- a/library/std/src/sys/pal/unix/weak.rs +++ b/library/std/src/sys/pal/unix/weak.rs @@ -23,9 +23,8 @@ use crate::ffi::CStr; use crate::marker::PhantomData; -use crate::mem; -use crate::ptr; use crate::sync::atomic::{self, AtomicPtr, Ordering}; +use crate::{mem, ptr}; // We can use true weak linkage on ELF targets. #[cfg(all(unix, not(target_vendor = "apple")))] @@ -169,14 +168,7 @@ pub(crate) macro syscall { if let Some(fun) = $name.get() { fun($($arg_name),*) } else { - // This looks like a hack, but concat_idents only accepts idents - // (not paths). - use libc::*; - - syscall( - concat_idents!(SYS_, $name), - $($arg_name),* - ) as $ret + libc::syscall(libc::${concat(SYS_, $name)}, $($arg_name),*) as $ret } } ) @@ -186,14 +178,7 @@ pub(crate) macro syscall { pub(crate) macro raw_syscall { (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( unsafe fn $name($($arg_name:$t),*) -> $ret { - // This looks like a hack, but concat_idents only accepts idents - // (not paths). - use libc::*; - - syscall( - concat_idents!(SYS_, $name), - $($arg_name),* - ) as $ret + libc::syscall(libc::${concat(SYS_, $name)}, $($arg_name),*) as $ret } ) } diff --git a/library/std/src/sys/pal/unsupported/fs.rs b/library/std/src/sys/pal/unsupported/fs.rs index 6ac1b5d2bcfca..474c9fe97d18d 100644 --- a/library/std/src/sys/pal/unsupported/fs.rs +++ b/library/std/src/sys/pal/unsupported/fs.rs @@ -291,7 +291,7 @@ pub fn remove_dir_all(_path: &Path) -> io::Result<()> { unsupported() } -pub fn try_exists(_path: &Path) -> io::Result { +pub fn exists(_path: &Path) -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index 01f5cfd429753..442e6042ad561 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -11,9 +11,6 @@ pub mod pipe; pub mod process; pub mod stdio; pub mod thread; -#[cfg(target_thread_local)] -pub mod thread_local_dtor; -pub mod thread_local_key; pub mod time; mod common; diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs index 248b34829f2ee..481fd62c04fe8 100644 --- a/library/std/src/sys/pal/unsupported/os.rs +++ b/library/std/src/sys/pal/unsupported/os.rs @@ -1,10 +1,9 @@ use super::unsupported; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::io; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; +use crate::{fmt, io}; pub fn errno() -> i32 { 0 @@ -96,11 +95,11 @@ pub fn getenv(_: &OsStr) -> Option { None } -pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } -pub fn unsetenv(_: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } diff --git a/library/std/src/sys/pal/unsupported/pipe.rs b/library/std/src/sys/pal/unsupported/pipe.rs index d7d8f297ae586..6799d21a1ff75 100644 --- a/library/std/src/sys/pal/unsupported/pipe.rs +++ b/library/std/src/sys/pal/unsupported/pipe.rs @@ -1,8 +1,19 @@ +use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; pub struct AnonPipe(!); +impl fmt::Debug for AnonPipe { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + impl AnonPipe { + pub fn try_clone(&self) -> io::Result { + self.0 + } + pub fn read(&self, _buf: &mut [u8]) -> io::Result { self.0 } diff --git a/library/std/src/sys/pal/unsupported/process.rs b/library/std/src/sys/pal/unsupported/process.rs index 2445d9073dbb9..40231bfc90b1e 100644 --- a/library/std/src/sys/pal/unsupported/process.rs +++ b/library/std/src/sys/pal/unsupported/process.rs @@ -1,14 +1,12 @@ +pub use crate::ffi::OsString as EnvKey; use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::io; use crate::num::NonZero; use crate::path::Path; use crate::sys::fs::File; use crate::sys::pipe::AnonPipe; use crate::sys::unsupported; use crate::sys_common::process::{CommandEnv, CommandEnvs}; - -pub use crate::ffi::OsString as EnvKey; +use crate::{fmt, io}; //////////////////////////////////////////////////////////////////////////////// // Command diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs index ea939247199c2..89f8bad7026ee 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/pal/unsupported/thread.rs @@ -6,7 +6,7 @@ use crate::time::Duration; pub struct Thread(!); -pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; +pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements diff --git a/library/std/src/sys/pal/unsupported/thread_local_dtor.rs b/library/std/src/sys/pal/unsupported/thread_local_dtor.rs deleted file mode 100644 index 84660ea588156..0000000000000 --- a/library/std/src/sys/pal/unsupported/thread_local_dtor.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![unstable(feature = "thread_local_internals", issue = "none")] - -#[cfg_attr(target_family = "wasm", allow(unused))] // unused on wasm32-unknown-unknown -pub unsafe fn register_dtor(_t: *mut u8, _dtor: unsafe extern "C" fn(*mut u8)) { - // FIXME: right now there is no concept of "thread exit", but this is likely - // going to show up at some point in the form of an exported symbol that the - // wasm runtime is going to be expected to call. For now we basically just - // ignore the arguments, but if such a function starts to exist it will - // likely look like the OSX implementation in `unix/fast_thread_local.rs` -} diff --git a/library/std/src/sys/pal/unsupported/thread_local_key.rs b/library/std/src/sys/pal/unsupported/thread_local_key.rs deleted file mode 100644 index b6e5e4cd2e197..0000000000000 --- a/library/std/src/sys/pal/unsupported/thread_local_key.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub type Key = usize; - -#[inline] -pub unsafe fn create(_dtor: Option) -> Key { - panic!("should not be used on this target"); -} - -#[inline] -pub unsafe fn set(_key: Key, _value: *mut u8) { - panic!("should not be used on this target"); -} - -#[inline] -pub unsafe fn get(_key: Key) -> *mut u8 { - panic!("should not be used on this target"); -} - -#[inline] -pub unsafe fn destroy(_key: Key) { - panic!("should not be used on this target"); -} diff --git a/library/std/src/sys/pal/wasi/args.rs b/library/std/src/sys/pal/wasi/args.rs index c42c310e3a254..6b6d1b8ff4e2e 100644 --- a/library/std/src/sys/pal/wasi/args.rs +++ b/library/std/src/sys/pal/wasi/args.rs @@ -1,9 +1,8 @@ #![deny(unsafe_op_in_unsafe_fn)] use crate::ffi::{CStr, OsStr, OsString}; -use crate::fmt; use crate::os::wasi::ffi::OsStrExt; -use crate::vec; +use crate::{fmt, vec}; pub struct Args { iter: vec::IntoIter, diff --git a/library/std/src/sys/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs index 529b82e019893..11900886f0b5c 100644 --- a/library/std/src/sys/pal/wasi/fs.rs +++ b/library/std/src/sys/pal/wasi/fs.rs @@ -2,22 +2,19 @@ use super::fd::WasiFd; use crate::ffi::{CStr, OsStr, OsString}; -use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::iter; use crate::mem::{self, ManuallyDrop}; use crate::os::raw::c_int; use crate::os::wasi::ffi::{OsStrExt, OsStringExt}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; -use crate::ptr; use crate::sync::Arc; use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::time::SystemTime; use crate::sys::unsupported; +pub use crate::sys_common::fs::exists; use crate::sys_common::{AsInner, FromInner, IntoInner}; - -pub use crate::sys_common::fs::try_exists; +use crate::{fmt, iter, ptr}; pub struct File { fd: WasiFd, diff --git a/library/std/src/sys/pal/wasi/helpers.rs b/library/std/src/sys/pal/wasi/helpers.rs index 82149cef8fad1..4b770ee23bc5d 100644 --- a/library/std/src/sys/pal/wasi/helpers.rs +++ b/library/std/src/sys/pal/wasi/helpers.rs @@ -1,5 +1,4 @@ -use crate::io as std_io; -use crate::mem; +use crate::{io as std_io, mem}; #[inline] pub fn is_interrupted(errno: i32) -> bool { diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index c1266619b36ab..f4dc3ebd4140b 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -33,24 +33,20 @@ pub mod pipe; pub mod process; pub mod stdio; pub mod thread; -#[path = "../unsupported/thread_local_dtor.rs"] -pub mod thread_local_dtor; -#[path = "../unsupported/thread_local_key.rs"] -pub mod thread_local_key; pub mod time; #[path = "../unsupported/common.rs"] #[deny(unsafe_op_in_unsafe_fn)] #[allow(unused)] mod common; + pub use common::*; mod helpers; -// These exports are listed individually to work around Rust's glob import -// conflict rules. If we glob export `helpers` and `common` together, then -// the compiler complains about conflicts. -pub use helpers::abort_internal; -pub use helpers::decode_error_kind; + +// The following exports are listed individually to work around Rust's glob +// import conflict rules. If we glob export `helpers` and `common` together, +// then the compiler complains about conflicts. + use helpers::err2io; -pub use helpers::hashmap_random_keys; -pub use helpers::is_interrupted; +pub use helpers::{abort_internal, decode_error_kind, hashmap_random_keys, is_interrupted}; diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index ee377b6ef791d..f5b17d9df94b4 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -1,18 +1,16 @@ #![deny(unsafe_op_in_unsafe_fn)] +use core::slice::memchr; + use crate::error::Error as StdError; use crate::ffi::{CStr, OsStr, OsString}; -use crate::fmt; -use crate::io; use crate::marker::PhantomData; use crate::ops::Drop; use crate::os::wasi::prelude::*; use crate::path::{self, PathBuf}; -use crate::str; use crate::sys::common::small_c_string::{run_path_with_cstr, run_with_cstr}; use crate::sys::unsupported; -use crate::vec; -use core::slice::memchr; +use crate::{fmt, io, str, vec}; // Add a few symbols not in upstream `libc` just yet. mod libc { @@ -244,7 +242,7 @@ pub fn getenv(k: &OsStr) -> Option { .flatten() } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { run_with_cstr(k.as_bytes(), &|k| { run_with_cstr(v.as_bytes(), &|v| unsafe { let _guard = env_write_lock(); @@ -253,7 +251,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { }) } -pub fn unsetenv(n: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { run_with_cstr(n.as_bytes(), &|nbuf| unsafe { let _guard = env_write_lock(); cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index d45fb28b67e63..c37acd8dfeeb7 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -1,9 +1,8 @@ use crate::ffi::CStr; -use crate::io; -use crate::mem; use crate::num::NonZero; use crate::sys::unsupported; use crate::time::Duration; +use crate::{io, mem}; cfg_if::cfg_if! { if #[cfg(target_feature = "atomics")] { @@ -66,7 +65,7 @@ cfg_if::cfg_if! { } } -pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; +pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements @@ -172,12 +171,10 @@ impl Thread { pub fn join(self) { cfg_if::cfg_if! { if #[cfg(target_feature = "atomics")] { - unsafe { - let ret = libc::pthread_join(self.id, ptr::null_mut()); - mem::forget(self); - if ret != 0 { - rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); - } + let id = mem::ManuallyDrop::new(self).id; + let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; + if ret != 0 { + rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); } } else { self.0 diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 6787ffb4bed8f..f20630e10cff9 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -34,10 +34,6 @@ pub mod process; pub mod stdio; #[path = "../wasi/thread.rs"] pub mod thread; -#[path = "../unsupported/thread_local_dtor.rs"] -pub mod thread_local_dtor; -#[path = "../unsupported/thread_local_key.rs"] -pub mod thread_local_key; #[path = "../wasi/time.rs"] pub mod time; @@ -45,17 +41,17 @@ pub mod time; #[deny(unsafe_op_in_unsafe_fn)] #[allow(unused)] mod common; + pub use common::*; #[path = "../wasi/helpers.rs"] mod helpers; -// These exports are listed individually to work around Rust's glob import -// conflict rules. If we glob export `helpers` and `common` together, then -// the compiler complains about conflicts. -pub use helpers::abort_internal; -pub use helpers::decode_error_kind; + +// The following exports are listed individually to work around Rust's glob +// import conflict rules. If we glob export `helpers` and `common` together, +// then the compiler complains about conflicts. + use helpers::err2io; -pub use helpers::hashmap_random_keys; -pub use helpers::is_interrupted; +pub use helpers::{abort_internal, decode_error_kind, hashmap_random_keys, is_interrupted}; mod cabi_realloc; diff --git a/library/std/src/sys/pal/wasm/alloc.rs b/library/std/src/sys/pal/wasm/alloc.rs index b74ce0d47425a..ef9d753d7f86c 100644 --- a/library/std/src/sys/pal/wasm/alloc.rs +++ b/library/std/src/sys/pal/wasm/alloc.rs @@ -57,10 +57,8 @@ unsafe impl GlobalAlloc for System { #[cfg(target_feature = "atomics")] mod lock { - use crate::sync::atomic::{ - AtomicI32, - Ordering::{Acquire, Release}, - }; + use crate::sync::atomic::AtomicI32; + use crate::sync::atomic::Ordering::{Acquire, Release}; static LOCKED: AtomicI32 = AtomicI32::new(0); diff --git a/library/std/src/sys/pal/wasm/atomics/futex.rs b/library/std/src/sys/pal/wasm/atomics/futex.rs index f4fbe9f48554b..42913a99ee9d6 100644 --- a/library/std/src/sys/pal/wasm/atomics/futex.rs +++ b/library/std/src/sys/pal/wasm/atomics/futex.rs @@ -1,7 +1,16 @@ -use crate::arch::wasm32; +#[cfg(target_arch = "wasm32")] +use core::arch::wasm32 as wasm; +#[cfg(target_arch = "wasm64")] +use core::arch::wasm64 as wasm; + use crate::sync::atomic::AtomicU32; use crate::time::Duration; +/// An atomic for use as a futex that is at least 8-bits but may be larger. +pub type SmallAtomic = AtomicU32; +/// Must be the underlying type of SmallAtomic +pub type SmallPrimitive = u32; + /// Wait for a futex_wake operation to wake us. /// /// Returns directly if the futex doesn't hold the expected value. @@ -10,25 +19,22 @@ use crate::time::Duration; pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { let timeout = timeout.and_then(|t| t.as_nanos().try_into().ok()).unwrap_or(-1); unsafe { - wasm32::memory_atomic_wait32( - futex as *const AtomicU32 as *mut i32, - expected as i32, - timeout, - ) < 2 + wasm::memory_atomic_wait32(futex as *const AtomicU32 as *mut i32, expected as i32, timeout) + < 2 } } -/// Wake up one thread that's blocked on futex_wait on this futex. +/// Wakes up one thread that's blocked on `futex_wait` on this futex. /// /// Returns true if this actually woke up such a thread, /// or false if no thread was waiting on this futex. pub fn futex_wake(futex: &AtomicU32) -> bool { - unsafe { wasm32::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, 1) > 0 } + unsafe { wasm::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, 1) > 0 } } -/// Wake up all threads that are waiting on futex_wait on this futex. +/// Wakes up all threads that are waiting on `futex_wait` on this futex. pub fn futex_wake_all(futex: &AtomicU32) { unsafe { - wasm32::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, i32::MAX as u32); + wasm::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, i32::MAX as u32); } } diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs index 49f936f14498c..afdb159fe6f8b 100644 --- a/library/std/src/sys/pal/wasm/atomics/thread.rs +++ b/library/std/src/sys/pal/wasm/atomics/thread.rs @@ -6,7 +6,7 @@ use crate::time::Duration; pub struct Thread(!); -pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; +pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements @@ -19,7 +19,11 @@ impl Thread { pub fn set_name(_name: &CStr) {} pub fn sleep(dur: Duration) { - use crate::arch::wasm32; + #[cfg(target_arch = "wasm32")] + use core::arch::wasm32 as wasm; + #[cfg(target_arch = "wasm64")] + use core::arch::wasm64 as wasm; + use crate::cmp; // Use an atomic wait to block the current thread artificially with a @@ -31,7 +35,7 @@ impl Thread { while nanos > 0 { let amt = cmp::min(i64::MAX as u128, nanos); let mut x = 0; - let val = unsafe { wasm32::memory_atomic_wait32(&mut x, 0, amt as i64) }; + let val = unsafe { wasm::memory_atomic_wait32(&mut x, 0, amt as i64) }; debug_assert_eq!(val, 2); nanos -= amt; } diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 75dd10826cc04..4c34859e918bb 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -34,10 +34,6 @@ pub mod pipe; pub mod process; #[path = "../unsupported/stdio.rs"] pub mod stdio; -#[path = "../unsupported/thread_local_dtor.rs"] -pub mod thread_local_dtor; -#[path = "../unsupported/thread_local_key.rs"] -pub mod thread_local_key; #[path = "../unsupported/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/windows/alloc.rs b/library/std/src/sys/pal/windows/alloc.rs index 681d1a5efe932..92b68b26032c6 100644 --- a/library/std/src/sys/pal/windows/alloc.rs +++ b/library/std/src/sys/pal/windows/alloc.rs @@ -1,12 +1,11 @@ -#![deny(unsafe_op_in_unsafe_fn)] +use core::mem::MaybeUninit; use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ffi::c_void; use crate::ptr; use crate::sync::atomic::{AtomicPtr, Ordering}; -use crate::sys::c; +use crate::sys::c::{self, windows_targets}; use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; -use core::mem::MaybeUninit; #[cfg(test)] mod tests; @@ -15,76 +14,73 @@ mod tests; // See https://docs.microsoft.com/windows/win32/api/heapapi/ // Flag to indicate that the memory returned by `HeapAlloc` should be zeroed. -const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008; +const HEAP_ZERO_MEMORY: u32 = 0x00000008; -#[link(name = "kernel32")] -extern "system" { - // Get a handle to the default heap of the current process, or null if the operation fails. - // - // SAFETY: Successful calls to this function within the same process are assumed to - // always return the same handle, which remains valid for the entire lifetime of the process. - // - // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap - fn GetProcessHeap() -> c::HANDLE; +// Get a handle to the default heap of the current process, or null if the operation fails. +// +// SAFETY: Successful calls to this function within the same process are assumed to +// always return the same handle, which remains valid for the entire lifetime of the process. +// +// See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap +windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> c::HANDLE); - // Allocate a block of `dwBytes` bytes of memory from a given heap `hHeap`. - // The allocated memory may be uninitialized, or zeroed if `dwFlags` is - // set to `HEAP_ZERO_MEMORY`. - // - // Returns a pointer to the newly-allocated memory or null if the operation fails. - // The returned pointer will be aligned to at least `MIN_ALIGN`. - // - // SAFETY: - // - `hHeap` must be a non-null handle returned by `GetProcessHeap`. - // - `dwFlags` must be set to either zero or `HEAP_ZERO_MEMORY`. - // - // Note that `dwBytes` is allowed to be zero, contrary to some other allocators. - // - // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapalloc - fn HeapAlloc(hHeap: c::HANDLE, dwFlags: c::DWORD, dwBytes: c::SIZE_T) -> c::LPVOID; +// Allocate a block of `dwBytes` bytes of memory from a given heap `hHeap`. +// The allocated memory may be uninitialized, or zeroed if `dwFlags` is +// set to `HEAP_ZERO_MEMORY`. +// +// Returns a pointer to the newly-allocated memory or null if the operation fails. +// The returned pointer will be aligned to at least `MIN_ALIGN`. +// +// SAFETY: +// - `hHeap` must be a non-null handle returned by `GetProcessHeap`. +// - `dwFlags` must be set to either zero or `HEAP_ZERO_MEMORY`. +// +// Note that `dwBytes` is allowed to be zero, contrary to some other allocators. +// +// See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapalloc +windows_targets::link!("kernel32.dll" "system" fn HeapAlloc(hheap: c::HANDLE, dwflags: u32, dwbytes: usize) -> *mut c_void); - // Reallocate a block of memory behind a given pointer `lpMem` from a given heap `hHeap`, - // to a block of at least `dwBytes` bytes, either shrinking the block in place, - // or allocating at a new location, copying memory, and freeing the original location. - // - // Returns a pointer to the reallocated memory or null if the operation fails. - // The returned pointer will be aligned to at least `MIN_ALIGN`. - // If the operation fails the given block will never have been freed. - // - // SAFETY: - // - `hHeap` must be a non-null handle returned by `GetProcessHeap`. - // - `dwFlags` must be set to zero. - // - `lpMem` must be a non-null pointer to an allocated block returned by `HeapAlloc` or - // `HeapReAlloc`, that has not already been freed. - // If the block was successfully reallocated at a new location, pointers pointing to - // the freed memory, such as `lpMem`, must not be dereferenced ever again. - // - // Note that `dwBytes` is allowed to be zero, contrary to some other allocators. - // - // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heaprealloc - fn HeapReAlloc( - hHeap: c::HANDLE, - dwFlags: c::DWORD, - lpMem: c::LPVOID, - dwBytes: c::SIZE_T, - ) -> c::LPVOID; +// Reallocate a block of memory behind a given pointer `lpMem` from a given heap `hHeap`, +// to a block of at least `dwBytes` bytes, either shrinking the block in place, +// or allocating at a new location, copying memory, and freeing the original location. +// +// Returns a pointer to the reallocated memory or null if the operation fails. +// The returned pointer will be aligned to at least `MIN_ALIGN`. +// If the operation fails the given block will never have been freed. +// +// SAFETY: +// - `hHeap` must be a non-null handle returned by `GetProcessHeap`. +// - `dwFlags` must be set to zero. +// - `lpMem` must be a non-null pointer to an allocated block returned by `HeapAlloc` or +// `HeapReAlloc`, that has not already been freed. +// If the block was successfully reallocated at a new location, pointers pointing to +// the freed memory, such as `lpMem`, must not be dereferenced ever again. +// +// Note that `dwBytes` is allowed to be zero, contrary to some other allocators. +// +// See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heaprealloc +windows_targets::link!("kernel32.dll" "system" fn HeapReAlloc( + hheap: c::HANDLE, + dwflags : u32, + lpmem: *const c_void, + dwbytes: usize +) -> *mut c_void); - // Free a block of memory behind a given pointer `lpMem` from a given heap `hHeap`. - // Returns a nonzero value if the operation is successful, and zero if the operation fails. - // - // SAFETY: - // - `hHeap` must be a non-null handle returned by `GetProcessHeap`. - // - `dwFlags` must be set to zero. - // - `lpMem` must be a pointer to an allocated block returned by `HeapAlloc` or `HeapReAlloc`, - // that has not already been freed. - // If the block was successfully freed, pointers pointing to the freed memory, such as `lpMem`, - // must not be dereferenced ever again. - // - // Note that `lpMem` is allowed to be null, which will not cause the operation to fail. - // - // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapfree - fn HeapFree(hHeap: c::HANDLE, dwFlags: c::DWORD, lpMem: c::LPVOID) -> c::BOOL; -} +// Free a block of memory behind a given pointer `lpMem` from a given heap `hHeap`. +// Returns a nonzero value if the operation is successful, and zero if the operation fails. +// +// SAFETY: +// - `hHeap` must be a non-null handle returned by `GetProcessHeap`. +// - `dwFlags` must be set to zero. +// - `lpMem` must be a pointer to an allocated block returned by `HeapAlloc` or `HeapReAlloc`, +// that has not already been freed. +// If the block was successfully freed, pointers pointing to the freed memory, such as `lpMem`, +// must not be dereferenced ever again. +// +// Note that `lpMem` is allowed to be null, which will not cause the operation to fail. +// +// See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapfree +windows_targets::link!("kernel32.dll" "system" fn HeapFree(hheap: c::HANDLE, dwflags: u32, lpmem: *const c_void) -> c::BOOL); // Cached handle to the default heap of the current process. // Either a non-null handle returned by `GetProcessHeap`, or null when not yet initialized or `GetProcessHeap` failed. @@ -116,9 +112,9 @@ fn init_or_get_process_heap() -> c::HANDLE { #[cold] extern "C" fn process_heap_init_and_alloc( _heap: MaybeUninit, // We pass this argument to match the ABI of `HeapAlloc` - flags: c::DWORD, - dwBytes: c::SIZE_T, -) -> c::LPVOID { + flags: u32, + dwBytes: usize, +) -> *mut c_void { let heap = init_or_get_process_heap(); if core::intrinsics::unlikely(heap.is_null()) { return ptr::null_mut(); @@ -130,9 +126,9 @@ extern "C" fn process_heap_init_and_alloc( #[inline(never)] fn process_heap_alloc( _heap: MaybeUninit, // We pass this argument to match the ABI of `HeapAlloc`, - flags: c::DWORD, - dwBytes: c::SIZE_T, -) -> c::LPVOID { + flags: u32, + dwBytes: usize, +) -> *mut c_void { let heap = HEAP.load(Ordering::Relaxed); if core::intrinsics::likely(!heap.is_null()) { // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`. @@ -190,7 +186,7 @@ unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 { // it, it is safe to write a header directly before it. unsafe { ptr::write((aligned as *mut Header).sub(1), Header(ptr)) }; - // SAFETY: The returned pointer does not point to the to the start of an allocated block, + // SAFETY: The returned pointer does not point to the start of an allocated block, // but there is a header readable directly before it containing the location of the start // of the block. aligned @@ -243,7 +239,7 @@ unsafe impl GlobalAlloc for System { // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, // `block` is a pointer to the start of an allocated block. - unsafe { HeapFree(heap, 0, block as c::LPVOID) }; + unsafe { HeapFree(heap, 0, block.cast::()) }; } #[inline] @@ -256,7 +252,7 @@ unsafe impl GlobalAlloc for System { // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, // `ptr` is a pointer to the start of an allocated block. // The returned pointer points to the start of an allocated block. - unsafe { HeapReAlloc(heap, 0, ptr as c::LPVOID, new_size) as *mut u8 } + unsafe { HeapReAlloc(heap, 0, ptr.cast::(), new_size).cast::() } } else { // SAFETY: `realloc_fallback` is implemented using `dealloc` and `alloc`, which will // correctly handle `ptr` and return a pointer satisfying the guarantees of `System` diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index 555ad581b8568..00c816a6c09b8 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -227,8 +227,10 @@ pub fn set_file_information_by_handle( info: *const c_void, size: u32, ) -> Result<(), WinError> { - let result = c::SetFileInformationByHandle(handle, class, info, size); - (result != 0).then_some(()).ok_or_else(get_last_error) + unsafe { + let result = c::SetFileInformationByHandle(handle, class, info, size); + (result != 0).then_some(()).ok_or_else(get_last_error) + } } // SAFETY: The `SetFileInformation` trait ensures that this is safe. unsafe { set_info(handle, T::CLASS, info.as_ptr(), info.size()) } @@ -251,3 +253,39 @@ pub fn get_last_error() -> WinError { pub struct WinError { pub code: u32, } +impl WinError { + const fn new(code: u32) -> Self { + Self { code } + } +} + +// Error code constants. +// The constant names should be the same as the winapi constants except for the leading `ERROR_`. +// Due to the sheer number of codes, error codes should only be added here on an as-needed basis. +// However, they should never be removed as the assumption is they may be useful again in the future. +#[allow(unused)] +impl WinError { + /// Success is not an error. + /// Some Windows APIs do use this to distinguish between a zero return and an error return + /// but we should never return this to users as an error. + pub const SUCCESS: Self = Self::new(c::ERROR_SUCCESS); + // tidy-alphabetical-start + pub const ACCESS_DENIED: Self = Self::new(c::ERROR_ACCESS_DENIED); + pub const ALREADY_EXISTS: Self = Self::new(c::ERROR_ALREADY_EXISTS); + pub const CANT_ACCESS_FILE: Self = Self::new(c::ERROR_CANT_ACCESS_FILE); + pub const DELETE_PENDING: Self = Self::new(c::ERROR_DELETE_PENDING); + pub const DIRECTORY: Self = Self::new(c::ERROR_DIRECTORY); + pub const FILE_NOT_FOUND: Self = Self::new(c::ERROR_FILE_NOT_FOUND); + pub const INSUFFICIENT_BUFFER: Self = Self::new(c::ERROR_INSUFFICIENT_BUFFER); + pub const INVALID_FUNCTION: Self = Self::new(c::ERROR_INVALID_FUNCTION); + pub const INVALID_HANDLE: Self = Self::new(c::ERROR_INVALID_HANDLE); + pub const INVALID_PARAMETER: Self = Self::new(c::ERROR_INVALID_PARAMETER); + pub const NO_MORE_FILES: Self = Self::new(c::ERROR_NO_MORE_FILES); + pub const NOT_FOUND: Self = Self::new(c::ERROR_NOT_FOUND); + pub const NOT_SUPPORTED: Self = Self::new(c::ERROR_NOT_SUPPORTED); + pub const OPERATION_ABORTED: Self = Self::new(c::ERROR_OPERATION_ABORTED); + pub const PATH_NOT_FOUND: Self = Self::new(c::ERROR_PATH_NOT_FOUND); + pub const SHARING_VIOLATION: Self = Self::new(c::ERROR_SHARING_VIOLATION); + pub const TIMEOUT: Self = Self::new(c::ERROR_TIMEOUT); + // tidy-alphabetical-end +} diff --git a/library/std/src/sys/pal/windows/args.rs b/library/std/src/sys/pal/windows/args.rs index 5098c05196e25..66e75a8357149 100644 --- a/library/std/src/sys/pal/windows/args.rs +++ b/library/std/src/sys/pal/windows/args.rs @@ -8,8 +8,6 @@ mod tests; use super::os::current_exe; use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::io; use crate::num::NonZero; use crate::os::windows::prelude::*; use crate::path::{Path, PathBuf}; @@ -18,9 +16,7 @@ use crate::sys::process::ensure_no_nuls; use crate::sys::{c, to_u16s}; use crate::sys_common::wstr::WStrUnits; use crate::sys_common::AsInner; -use crate::vec; - -use crate::iter; +use crate::{fmt, io, iter, vec}; /// This is the const equivalent to `NonZero::new(n).unwrap()` /// diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 9d58ce05f018b..08b75186aef90 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -5,44 +5,15 @@ #![unstable(issue = "none", feature = "windows_c")] #![allow(clippy::style)] -use crate::ffi::CStr; -use crate::mem; -use crate::num::NonZero; -pub use crate::os::raw::c_int; -use crate::os::raw::{c_char, c_long, c_longlong, c_uint, c_ulong, c_ushort, c_void}; -use crate::os::windows::io::{AsRawHandle, BorrowedHandle}; -use crate::ptr; +use core::ffi::{c_uint, c_ulong, c_ushort, c_void, CStr}; +use core::{mem, ptr}; + +pub(super) mod windows_targets; mod windows_sys; pub use windows_sys::*; -pub type DWORD = c_ulong; -pub type NonZeroDWORD = NonZero; -pub type LARGE_INTEGER = c_longlong; -#[cfg_attr(target_vendor = "uwp", allow(unused))] -pub type LONG = c_long; -pub type UINT = c_uint; pub type WCHAR = u16; -pub type USHORT = c_ushort; -pub type SIZE_T = usize; -pub type CHAR = c_char; -pub type ULONG = c_ulong; - -pub type LPCVOID = *const c_void; -pub type LPOVERLAPPED = *mut OVERLAPPED; -pub type LPSECURITY_ATTRIBUTES = *mut SECURITY_ATTRIBUTES; -pub type LPVOID = *mut c_void; -pub type LPWCH = *mut WCHAR; -pub type LPWSTR = *mut WCHAR; - -#[cfg(target_vendor = "win7")] -pub type PSRWLOCK = *mut SRWLOCK; - -pub type socklen_t = c_int; -pub type ADDRESS_FAMILY = USHORT; -pub use FD_SET as fd_set; -pub use LINGER as linger; -pub use TIMEVAL as timeval; pub const INVALID_HANDLE_VALUE: HANDLE = ::core::ptr::without_provenance_mut(-1i32 as _); @@ -54,26 +25,13 @@ pub const EXIT_FAILURE: u32 = 1; pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE { Ptr: ptr::null_mut() }; #[cfg(target_vendor = "win7")] pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { Ptr: ptr::null_mut() }; +#[cfg(not(target_thread_local))] pub const INIT_ONCE_STATIC_INIT: INIT_ONCE = INIT_ONCE { Ptr: ptr::null_mut() }; // Some windows_sys types have different signs than the types we use. pub const OBJ_DONT_REPARSE: u32 = windows_sys::OBJ_DONT_REPARSE as u32; pub const FRS_ERR_SYSVOL_POPULATE_TIMEOUT: u32 = windows_sys::FRS_ERR_SYSVOL_POPULATE_TIMEOUT as u32; -pub const AF_INET: c_int = windows_sys::AF_INET as c_int; -pub const AF_INET6: c_int = windows_sys::AF_INET6 as c_int; - -#[repr(C)] -pub struct ip_mreq { - pub imr_multiaddr: in_addr, - pub imr_interface: in_addr, -} - -#[repr(C)] -pub struct ipv6_mreq { - pub ipv6mr_multiaddr: in6_addr, - pub ipv6mr_interface: c_uint, -} // Equivalent to the `NT_SUCCESS` C preprocessor macro. // See: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values @@ -145,45 +103,6 @@ pub struct MOUNT_POINT_REPARSE_BUFFER { pub PathBuffer: WCHAR, } -#[repr(C)] -pub struct SOCKADDR_STORAGE_LH { - pub ss_family: ADDRESS_FAMILY, - pub __ss_pad1: [CHAR; 6], - pub __ss_align: i64, - pub __ss_pad2: [CHAR; 112], -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct sockaddr_in { - pub sin_family: ADDRESS_FAMILY, - pub sin_port: USHORT, - pub sin_addr: in_addr, - pub sin_zero: [CHAR; 8], -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct sockaddr_in6 { - pub sin6_family: ADDRESS_FAMILY, - pub sin6_port: USHORT, - pub sin6_flowinfo: c_ulong, - pub sin6_addr: in6_addr, - pub sin6_scope_id: c_ulong, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct in_addr { - pub s_addr: u32, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct in6_addr { - pub s6_addr: [u8; 16], -} - // Desktop specific functions & types cfg_if::cfg_if! { if #[cfg(not(target_vendor = "uwp"))] { @@ -191,125 +110,6 @@ if #[cfg(not(target_vendor = "uwp"))] { } } -pub unsafe extern "system" fn WriteFileEx( - hFile: BorrowedHandle<'_>, - lpBuffer: *mut ::core::ffi::c_void, - nNumberOfBytesToWrite: u32, - lpOverlapped: *mut OVERLAPPED, - lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, -) -> BOOL { - windows_sys::WriteFileEx( - hFile.as_raw_handle(), - lpBuffer.cast::(), - nNumberOfBytesToWrite, - lpOverlapped, - lpCompletionRoutine, - ) -} - -pub unsafe extern "system" fn ReadFileEx( - hFile: BorrowedHandle<'_>, - lpBuffer: *mut ::core::ffi::c_void, - nNumberOfBytesToRead: u32, - lpOverlapped: *mut OVERLAPPED, - lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, -) -> BOOL { - windows_sys::ReadFileEx( - hFile.as_raw_handle(), - lpBuffer.cast::(), - nNumberOfBytesToRead, - lpOverlapped, - lpCompletionRoutine, - ) -} - -// POSIX compatibility shims. -pub unsafe fn recv(socket: SOCKET, buf: *mut c_void, len: c_int, flags: c_int) -> c_int { - windows_sys::recv(socket, buf.cast::(), len, flags) -} -pub unsafe fn send(socket: SOCKET, buf: *const c_void, len: c_int, flags: c_int) -> c_int { - windows_sys::send(socket, buf.cast::(), len, flags) -} -pub unsafe fn recvfrom( - socket: SOCKET, - buf: *mut c_void, - len: c_int, - flags: c_int, - addr: *mut SOCKADDR, - addrlen: *mut c_int, -) -> c_int { - windows_sys::recvfrom(socket, buf.cast::(), len, flags, addr, addrlen) -} -pub unsafe fn sendto( - socket: SOCKET, - buf: *const c_void, - len: c_int, - flags: c_int, - addr: *const SOCKADDR, - addrlen: c_int, -) -> c_int { - windows_sys::sendto(socket, buf.cast::(), len, flags, addr, addrlen) -} -pub unsafe fn getaddrinfo( - node: *const c_char, - service: *const c_char, - hints: *const ADDRINFOA, - res: *mut *mut ADDRINFOA, -) -> c_int { - windows_sys::getaddrinfo(node.cast::(), service.cast::(), hints, res) -} - -cfg_if::cfg_if! { -if #[cfg(not(target_vendor = "uwp"))] { -pub unsafe fn NtReadFile( - filehandle: BorrowedHandle<'_>, - event: HANDLE, - apcroutine: PIO_APC_ROUTINE, - apccontext: *mut c_void, - iostatusblock: &mut IO_STATUS_BLOCK, - buffer: *mut crate::mem::MaybeUninit, - length: ULONG, - byteoffset: Option<&LARGE_INTEGER>, - key: Option<&ULONG>, -) -> NTSTATUS { - windows_sys::NtReadFile( - filehandle.as_raw_handle(), - event, - apcroutine, - apccontext, - iostatusblock, - buffer.cast::(), - length, - byteoffset.map(|o| o as *const i64).unwrap_or(ptr::null()), - key.map(|k| k as *const u32).unwrap_or(ptr::null()), - ) -} -pub unsafe fn NtWriteFile( - filehandle: BorrowedHandle<'_>, - event: HANDLE, - apcroutine: PIO_APC_ROUTINE, - apccontext: *mut c_void, - iostatusblock: &mut IO_STATUS_BLOCK, - buffer: *const u8, - length: ULONG, - byteoffset: Option<&LARGE_INTEGER>, - key: Option<&ULONG>, -) -> NTSTATUS { - windows_sys::NtWriteFile( - filehandle.as_raw_handle(), - event, - apcroutine, - apccontext, - iostatusblock, - buffer.cast::(), - length, - byteoffset.map(|o| o as *const i64).unwrap_or(ptr::null()), - key.map(|k| k as *const u32).unwrap_or(ptr::null()), - ) -} -} -} - // Use raw-dylib to import ProcessPrng as we can't rely on there being an import library. cfg_if::cfg_if! { if #[cfg(not(target_vendor = "win7"))] { @@ -333,26 +133,26 @@ compat_fn_with_fallback! { // >= Win10 1607 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription pub fn SetThreadDescription(hthread: HANDLE, lpthreaddescription: PCWSTR) -> HRESULT { - SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); E_NOTIMPL + unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as u32); E_NOTIMPL } } // >= Win10 1607 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreaddescription pub fn GetThreadDescription(hthread: HANDLE, lpthreaddescription: *mut PWSTR) -> HRESULT { - SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); E_NOTIMPL + unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as u32); E_NOTIMPL } } // >= Win8 / Server 2012 // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime #[cfg(target_vendor = "win7")] pub fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime: *mut FILETIME) -> () { - GetSystemTimeAsFileTime(lpsystemtimeasfiletime) + unsafe { GetSystemTimeAsFileTime(lpsystemtimeasfiletime) } } // >= Win11 / Server 2022 // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2a pub fn GetTempPath2W(bufferlength: u32, buffer: PWSTR) -> u32 { - GetTempPathW(bufferlength, buffer) + unsafe { GetTempPathW(bufferlength, buffer) } } } @@ -385,12 +185,12 @@ extern "system" { compat_fn_optional! { crate::sys::compat::load_synch_functions(); pub fn WaitOnAddress( - address: *const ::core::ffi::c_void, - compareaddress: *const ::core::ffi::c_void, + address: *const c_void, + compareaddress: *const c_void, addresssize: usize, dwmilliseconds: u32 ) -> BOOL; - pub fn WakeByAddressSingle(address: *const ::core::ffi::c_void); + pub fn WakeByAddressSingle(address: *const c_void); } #[cfg(any(target_vendor = "win7", target_vendor = "uwp"))] @@ -400,27 +200,27 @@ compat_fn_with_fallback! { #[cfg(target_vendor = "win7")] pub fn NtCreateKeyedEvent( KeyedEventHandle: *mut HANDLE, - DesiredAccess: DWORD, - ObjectAttributes: LPVOID, - Flags: ULONG + DesiredAccess: u32, + ObjectAttributes: *mut c_void, + Flags: u32 ) -> NTSTATUS { panic!("keyed events not available") } #[cfg(target_vendor = "win7")] pub fn NtReleaseKeyedEvent( EventHandle: HANDLE, - Key: LPVOID, + Key: *const c_void, Alertable: BOOLEAN, - Timeout: *mut c_longlong + Timeout: *mut i64 ) -> NTSTATUS { panic!("keyed events not available") } #[cfg(target_vendor = "win7")] pub fn NtWaitForKeyedEvent( EventHandle: HANDLE, - Key: LPVOID, + Key: *const c_void, Alertable: BOOLEAN, - Timeout: *mut c_longlong + Timeout: *mut i64 ) -> NTSTATUS { panic!("keyed events not available") } @@ -437,36 +237,36 @@ compat_fn_with_fallback! { shareaccess: FILE_SHARE_MODE, createdisposition: NTCREATEFILE_CREATE_DISPOSITION, createoptions: NTCREATEFILE_CREATE_OPTIONS, - eabuffer: *const ::core::ffi::c_void, + eabuffer: *const c_void, ealength: u32 ) -> NTSTATUS { STATUS_NOT_IMPLEMENTED } #[cfg(target_vendor = "uwp")] pub fn NtReadFile( - filehandle: BorrowedHandle<'_>, + filehandle: HANDLE, event: HANDLE, apcroutine: PIO_APC_ROUTINE, - apccontext: *mut c_void, - iostatusblock: &mut IO_STATUS_BLOCK, - buffer: *mut crate::mem::MaybeUninit, - length: ULONG, - byteoffset: Option<&LARGE_INTEGER>, - key: Option<&ULONG> + apccontext: *const c_void, + iostatusblock: *mut IO_STATUS_BLOCK, + buffer: *mut c_void, + length: u32, + byteoffset: *const i64, + key: *const u32 ) -> NTSTATUS { STATUS_NOT_IMPLEMENTED } #[cfg(target_vendor = "uwp")] pub fn NtWriteFile( - filehandle: BorrowedHandle<'_>, + filehandle: HANDLE, event: HANDLE, apcroutine: PIO_APC_ROUTINE, - apccontext: *mut c_void, - iostatusblock: &mut IO_STATUS_BLOCK, - buffer: *const u8, - length: ULONG, - byteoffset: Option<&LARGE_INTEGER>, - key: Option<&ULONG> + apccontext: *const c_void, + iostatusblock: *mut IO_STATUS_BLOCK, + buffer: *const c_void, + length: u32, + byteoffset: *const i64, + key: *const u32 ) -> NTSTATUS { STATUS_NOT_IMPLEMENTED } @@ -475,47 +275,3 @@ compat_fn_with_fallback! { Status as u32 } } - -// # Arm32 shim -// -// AddVectoredExceptionHandler and WSAStartup use platform-specific types. -// However, Microsoft no longer supports thumbv7a so definitions for those targets -// are not included in the win32 metadata. We work around that by defining them here. -// -// Where possible, these definitions should be kept in sync with https://docs.rs/windows-sys -cfg_if::cfg_if! { -if #[cfg(not(target_vendor = "uwp"))] { - #[link(name = "kernel32")] - extern "system" { - pub fn AddVectoredExceptionHandler( - first: u32, - handler: PVECTORED_EXCEPTION_HANDLER, - ) -> *mut c_void; - } - pub type PVECTORED_EXCEPTION_HANDLER = Option< - unsafe extern "system" fn(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32, - >; - #[repr(C)] - pub struct EXCEPTION_POINTERS { - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ContextRecord: *mut CONTEXT, - } - #[cfg(target_arch = "arm")] - pub enum CONTEXT {} -}} - -#[link(name = "ws2_32")] -extern "system" { - pub fn WSAStartup(wversionrequested: u16, lpwsadata: *mut WSADATA) -> i32; -} -#[cfg(target_arch = "arm")] -#[repr(C)] -pub struct WSADATA { - pub wVersion: u16, - pub wHighVersion: u16, - pub szDescription: [u8; 257], - pub szSystemStatus: [u8; 129], - pub iMaxSockets: u16, - pub iMaxUdpDg: u16, - pub lpVendorInfo: PSTR, -} diff --git a/library/std/src/sys/pal/windows/c/README.md b/library/std/src/sys/pal/windows/c/README.md index d458e55efbcdd..efefc5faba7a4 100644 --- a/library/std/src/sys/pal/windows/c/README.md +++ b/library/std/src/sys/pal/windows/c/README.md @@ -3,7 +3,7 @@ be edited manually. To add bindings, edit `bindings.txt` then regenerate using the following command: - ./x run generate-windows-sys && ./x fmt library/std + ./x run generate-windows-sys && ./x fmt If you need to override generated functions or types then add them to `library/std/src/sys/pal/windows/c.rs`. diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index 849e64ac59135..afacc370c3420 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -1,5 +1,5 @@ --out windows_sys.rs ---config flatten std +--config flatten sys --filter !Windows.Win32.Foundation.INVALID_HANDLE_VALUE Windows.Wdk.Storage.FileSystem.FILE_COMPLETE_IF_OPLOCKED @@ -2059,6 +2059,7 @@ Windows.Win32.Networking.WinSock.SOCK_RDM Windows.Win32.Networking.WinSock.SOCK_SEQPACKET Windows.Win32.Networking.WinSock.SOCK_STREAM Windows.Win32.Networking.WinSock.SOCKADDR +Windows.Win32.Networking.WinSock.SOCKADDR_STORAGE Windows.Win32.Networking.WinSock.SOCKADDR_UN Windows.Win32.Networking.WinSock.SOCKET Windows.Win32.Networking.WinSock.SOCKET_ERROR @@ -2175,6 +2176,7 @@ Windows.Win32.Networking.WinSock.WSARecv Windows.Win32.Networking.WinSock.WSASend Windows.Win32.Networking.WinSock.WSASERVICE_NOT_FOUND Windows.Win32.Networking.WinSock.WSASocketW +Windows.Win32.Networking.WinSock.WSAStartup Windows.Win32.Networking.WinSock.WSASYSCALLFAILURE Windows.Win32.Networking.WinSock.WSASYSNOTREADY Windows.Win32.Networking.WinSock.WSATRY_AGAIN @@ -2419,6 +2421,7 @@ Windows.Win32.System.Console.STD_HANDLE Windows.Win32.System.Console.STD_INPUT_HANDLE Windows.Win32.System.Console.STD_OUTPUT_HANDLE Windows.Win32.System.Console.WriteConsoleW +Windows.Win32.System.Diagnostics.Debug.AddVectoredExceptionHandler Windows.Win32.System.Diagnostics.Debug.ARM64_NT_NEON128 Windows.Win32.System.Diagnostics.Debug.CONTEXT Windows.Win32.System.Diagnostics.Debug.EXCEPTION_RECORD @@ -2462,6 +2465,7 @@ Windows.Win32.System.LibraryLoader.GetProcAddress Windows.Win32.System.Performance.QueryPerformanceCounter Windows.Win32.System.Performance.QueryPerformanceFrequency Windows.Win32.System.Pipes.CreateNamedPipeW +Windows.Win32.System.Pipes.CreatePipe Windows.Win32.System.Pipes.NAMED_PIPE_MODE Windows.Win32.System.Pipes.PIPE_ACCEPT_REMOTE_CLIENTS Windows.Win32.System.Pipes.PIPE_CLIENT_END diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 1da8871ae44eb..9f22f54819509 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -1,846 +1,143 @@ -// Bindings generated by `windows-bindgen` 0.56.0 +// Bindings generated by `windows-bindgen` 0.58.0 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] -#[link(name = "advapi32")] -extern "system" { - pub fn OpenProcessToken( - processhandle: HANDLE, - desiredaccess: TOKEN_ACCESS_MASK, - tokenhandle: *mut HANDLE, - ) -> BOOL; -} -#[link(name = "advapi32")] -extern "system" { - #[link_name = "SystemFunction036"] - pub fn RtlGenRandom(randombuffer: *mut core::ffi::c_void, randombufferlength: u32) -> BOOLEAN; -} -#[link(name = "kernel32")] -extern "system" { - pub fn AcquireSRWLockExclusive(srwlock: *mut SRWLOCK); -} -#[link(name = "kernel32")] -extern "system" { - pub fn AcquireSRWLockShared(srwlock: *mut SRWLOCK); -} -#[link(name = "kernel32")] -extern "system" { - pub fn CancelIo(hfile: HANDLE) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CloseHandle(hobject: HANDLE) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CompareStringOrdinal( - lpstring1: PCWSTR, - cchcount1: i32, - lpstring2: PCWSTR, - cchcount2: i32, - bignorecase: BOOL, - ) -> COMPARESTRING_RESULT; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CopyFileExW( - lpexistingfilename: PCWSTR, - lpnewfilename: PCWSTR, - lpprogressroutine: LPPROGRESS_ROUTINE, - lpdata: *const core::ffi::c_void, - pbcancel: *mut BOOL, - dwcopyflags: u32, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateDirectoryW( - lppathname: PCWSTR, - lpsecurityattributes: *const SECURITY_ATTRIBUTES, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateEventW( - lpeventattributes: *const SECURITY_ATTRIBUTES, - bmanualreset: BOOL, - binitialstate: BOOL, - lpname: PCWSTR, - ) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateFileW( - lpfilename: PCWSTR, - dwdesiredaccess: u32, - dwsharemode: FILE_SHARE_MODE, - lpsecurityattributes: *const SECURITY_ATTRIBUTES, - dwcreationdisposition: FILE_CREATION_DISPOSITION, - dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, - htemplatefile: HANDLE, - ) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateHardLinkW( - lpfilename: PCWSTR, - lpexistingfilename: PCWSTR, - lpsecurityattributes: *const SECURITY_ATTRIBUTES, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateNamedPipeW( - lpname: PCWSTR, - dwopenmode: FILE_FLAGS_AND_ATTRIBUTES, - dwpipemode: NAMED_PIPE_MODE, - nmaxinstances: u32, - noutbuffersize: u32, - ninbuffersize: u32, - ndefaulttimeout: u32, - lpsecurityattributes: *const SECURITY_ATTRIBUTES, - ) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateProcessW( - lpapplicationname: PCWSTR, - lpcommandline: PWSTR, - lpprocessattributes: *const SECURITY_ATTRIBUTES, - lpthreadattributes: *const SECURITY_ATTRIBUTES, - binherithandles: BOOL, - dwcreationflags: PROCESS_CREATION_FLAGS, - lpenvironment: *const core::ffi::c_void, - lpcurrentdirectory: PCWSTR, - lpstartupinfo: *const STARTUPINFOW, - lpprocessinformation: *mut PROCESS_INFORMATION, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateSymbolicLinkW( - lpsymlinkfilename: PCWSTR, - lptargetfilename: PCWSTR, - dwflags: SYMBOLIC_LINK_FLAGS, - ) -> BOOLEAN; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateThread( - lpthreadattributes: *const SECURITY_ATTRIBUTES, - dwstacksize: usize, - lpstartaddress: LPTHREAD_START_ROUTINE, - lpparameter: *const core::ffi::c_void, - dwcreationflags: THREAD_CREATION_FLAGS, - lpthreadid: *mut u32, - ) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn CreateWaitableTimerExW( - lptimerattributes: *const SECURITY_ATTRIBUTES, - lptimername: PCWSTR, - dwflags: u32, - dwdesiredaccess: u32, - ) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn DeleteFileW(lpfilename: PCWSTR) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn DeleteProcThreadAttributeList(lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST); -} -#[link(name = "kernel32")] -extern "system" { - pub fn DeviceIoControl( - hdevice: HANDLE, - dwiocontrolcode: u32, - lpinbuffer: *const core::ffi::c_void, - ninbuffersize: u32, - lpoutbuffer: *mut core::ffi::c_void, - noutbuffersize: u32, - lpbytesreturned: *mut u32, - lpoverlapped: *mut OVERLAPPED, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn DuplicateHandle( - hsourceprocesshandle: HANDLE, - hsourcehandle: HANDLE, - htargetprocesshandle: HANDLE, - lptargethandle: *mut HANDLE, - dwdesiredaccess: u32, - binherithandle: BOOL, - dwoptions: DUPLICATE_HANDLE_OPTIONS, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn ExitProcess(uexitcode: u32) -> !; -} -#[link(name = "kernel32")] -extern "system" { - pub fn FindClose(hfindfile: HANDLE) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn FindFirstFileW(lpfilename: PCWSTR, lpfindfiledata: *mut WIN32_FIND_DATAW) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn FindNextFileW(hfindfile: HANDLE, lpfindfiledata: *mut WIN32_FIND_DATAW) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn FlushFileBuffers(hfile: HANDLE) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn FormatMessageW( - dwflags: FORMAT_MESSAGE_OPTIONS, - lpsource: *const core::ffi::c_void, - dwmessageid: u32, - dwlanguageid: u32, - lpbuffer: PWSTR, - nsize: u32, - arguments: *const *const i8, - ) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn FreeEnvironmentStringsW(penv: PCWSTR) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetActiveProcessorCount(groupnumber: u16) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetCommandLineW() -> PCWSTR; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetConsoleMode(hconsolehandle: HANDLE, lpmode: *mut CONSOLE_MODE) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetCurrentDirectoryW(nbufferlength: u32, lpbuffer: PWSTR) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetCurrentProcess() -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetCurrentProcessId() -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetCurrentThread() -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetEnvironmentStringsW() -> PWSTR; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetEnvironmentVariableW(lpname: PCWSTR, lpbuffer: PWSTR, nsize: u32) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetExitCodeProcess(hprocess: HANDLE, lpexitcode: *mut u32) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetFileAttributesW(lpfilename: PCWSTR) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetFileInformationByHandle( - hfile: HANDLE, - lpfileinformation: *mut BY_HANDLE_FILE_INFORMATION, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetFileInformationByHandleEx( - hfile: HANDLE, - fileinformationclass: FILE_INFO_BY_HANDLE_CLASS, - lpfileinformation: *mut core::ffi::c_void, - dwbuffersize: u32, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetFileType(hfile: HANDLE) -> FILE_TYPE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetFinalPathNameByHandleW( - hfile: HANDLE, - lpszfilepath: PWSTR, - cchfilepath: u32, - dwflags: GETFINALPATHNAMEBYHANDLE_FLAGS, - ) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetFullPathNameW( - lpfilename: PCWSTR, - nbufferlength: u32, - lpbuffer: PWSTR, - lpfilepart: *mut PWSTR, - ) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetLastError() -> WIN32_ERROR; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetModuleFileNameW(hmodule: HMODULE, lpfilename: PWSTR, nsize: u32) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetModuleHandleA(lpmodulename: PCSTR) -> HMODULE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetModuleHandleW(lpmodulename: PCWSTR) -> HMODULE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetOverlappedResult( - hfile: HANDLE, - lpoverlapped: *const OVERLAPPED, - lpnumberofbytestransferred: *mut u32, - bwait: BOOL, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetProcAddress(hmodule: HMODULE, lpprocname: PCSTR) -> FARPROC; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetProcessId(process: HANDLE) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetStdHandle(nstdhandle: STD_HANDLE) -> HANDLE; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetSystemDirectoryW(lpbuffer: PWSTR, usize: u32) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetSystemInfo(lpsysteminfo: *mut SYSTEM_INFO); -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetSystemTimeAsFileTime(lpsystemtimeasfiletime: *mut FILETIME); -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime: *mut FILETIME); -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetTempPathW(nbufferlength: u32, lpbuffer: PWSTR) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn GetWindowsDirectoryW(lpbuffer: PWSTR, usize: u32) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn InitOnceBeginInitialize( - lpinitonce: *mut INIT_ONCE, - dwflags: u32, - fpending: *mut BOOL, - lpcontext: *mut *mut core::ffi::c_void, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn InitOnceComplete( - lpinitonce: *mut INIT_ONCE, - dwflags: u32, - lpcontext: *const core::ffi::c_void, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn InitializeProcThreadAttributeList( - lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST, - dwattributecount: u32, - dwflags: u32, - lpsize: *mut usize, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn LocalFree(hmem: HLOCAL) -> HLOCAL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn MoveFileExW( - lpexistingfilename: PCWSTR, - lpnewfilename: PCWSTR, - dwflags: MOVE_FILE_FLAGS, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn MultiByteToWideChar( - codepage: u32, - dwflags: MULTI_BYTE_TO_WIDE_CHAR_FLAGS, - lpmultibytestr: PCSTR, - cbmultibyte: i32, - lpwidecharstr: PWSTR, - cchwidechar: i32, - ) -> i32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn QueryPerformanceCounter(lpperformancecount: *mut i64) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn QueryPerformanceFrequency(lpfrequency: *mut i64) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn ReadConsoleW( - hconsoleinput: HANDLE, - lpbuffer: *mut core::ffi::c_void, - nnumberofcharstoread: u32, - lpnumberofcharsread: *mut u32, - pinputcontrol: *const CONSOLE_READCONSOLE_CONTROL, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn ReadFile( - hfile: HANDLE, - lpbuffer: *mut u8, - nnumberofbytestoread: u32, - lpnumberofbytesread: *mut u32, - lpoverlapped: *mut OVERLAPPED, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn ReadFileEx( - hfile: HANDLE, - lpbuffer: *mut u8, - nnumberofbytestoread: u32, - lpoverlapped: *mut OVERLAPPED, - lpcompletionroutine: LPOVERLAPPED_COMPLETION_ROUTINE, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn ReleaseSRWLockExclusive(srwlock: *mut SRWLOCK); -} -#[link(name = "kernel32")] -extern "system" { - pub fn ReleaseSRWLockShared(srwlock: *mut SRWLOCK); -} -#[link(name = "kernel32")] -extern "system" { - pub fn RemoveDirectoryW(lppathname: PCWSTR) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetCurrentDirectoryW(lppathname: PCWSTR) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetEnvironmentVariableW(lpname: PCWSTR, lpvalue: PCWSTR) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetFileAttributesW( - lpfilename: PCWSTR, - dwfileattributes: FILE_FLAGS_AND_ATTRIBUTES, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetFileInformationByHandle( - hfile: HANDLE, - fileinformationclass: FILE_INFO_BY_HANDLE_CLASS, - lpfileinformation: *const core::ffi::c_void, - dwbuffersize: u32, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetFilePointerEx( - hfile: HANDLE, - lidistancetomove: i64, - lpnewfilepointer: *mut i64, - dwmovemethod: SET_FILE_POINTER_MOVE_METHOD, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetFileTime( - hfile: HANDLE, - lpcreationtime: *const FILETIME, - lplastaccesstime: *const FILETIME, - lplastwritetime: *const FILETIME, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetHandleInformation(hobject: HANDLE, dwmask: u32, dwflags: HANDLE_FLAGS) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetLastError(dwerrcode: WIN32_ERROR); -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetThreadStackGuarantee(stacksizeinbytes: *mut u32) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SetWaitableTimer( - htimer: HANDLE, - lpduetime: *const i64, - lperiod: i32, - pfncompletionroutine: PTIMERAPCROUTINE, - lpargtocompletionroutine: *const core::ffi::c_void, - fresume: BOOL, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn Sleep(dwmilliseconds: u32); -} -#[link(name = "kernel32")] -extern "system" { - pub fn SleepConditionVariableSRW( - conditionvariable: *mut CONDITION_VARIABLE, - srwlock: *mut SRWLOCK, - dwmilliseconds: u32, - flags: u32, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SleepEx(dwmilliseconds: u32, balertable: BOOL) -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn SwitchToThread() -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TerminateProcess(hprocess: HANDLE, uexitcode: u32) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TlsAlloc() -> u32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TlsFree(dwtlsindex: u32) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TlsGetValue(dwtlsindex: u32) -> *mut core::ffi::c_void; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TlsSetValue(dwtlsindex: u32, lptlsvalue: *const core::ffi::c_void) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TryAcquireSRWLockExclusive(srwlock: *mut SRWLOCK) -> BOOLEAN; -} -#[link(name = "kernel32")] -extern "system" { - pub fn TryAcquireSRWLockShared(srwlock: *mut SRWLOCK) -> BOOLEAN; -} -#[link(name = "kernel32")] -extern "system" { - pub fn UpdateProcThreadAttribute( - lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST, - dwflags: u32, - attribute: usize, - lpvalue: *const core::ffi::c_void, - cbsize: usize, - lppreviousvalue: *mut core::ffi::c_void, - lpreturnsize: *const usize, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn WaitForMultipleObjects( - ncount: u32, - lphandles: *const HANDLE, - bwaitall: BOOL, - dwmilliseconds: u32, - ) -> WAIT_EVENT; -} -#[link(name = "kernel32")] -extern "system" { - pub fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> WAIT_EVENT; -} -#[link(name = "kernel32")] -extern "system" { - pub fn WakeAllConditionVariable(conditionvariable: *mut CONDITION_VARIABLE); -} -#[link(name = "kernel32")] -extern "system" { - pub fn WakeConditionVariable(conditionvariable: *mut CONDITION_VARIABLE); -} -#[link(name = "kernel32")] -extern "system" { - pub fn WideCharToMultiByte( - codepage: u32, - dwflags: u32, - lpwidecharstr: PCWSTR, - cchwidechar: i32, - lpmultibytestr: PSTR, - cbmultibyte: i32, - lpdefaultchar: PCSTR, - lpuseddefaultchar: *mut BOOL, - ) -> i32; -} -#[link(name = "kernel32")] -extern "system" { - pub fn WriteConsoleW( - hconsoleoutput: HANDLE, - lpbuffer: *const core::ffi::c_void, - nnumberofcharstowrite: u32, - lpnumberofcharswritten: *mut u32, - lpreserved: *const core::ffi::c_void, - ) -> BOOL; -} -#[link(name = "kernel32")] -extern "system" { - pub fn WriteFileEx( - hfile: HANDLE, - lpbuffer: *const u8, - nnumberofbytestowrite: u32, - lpoverlapped: *mut OVERLAPPED, - lpcompletionroutine: LPOVERLAPPED_COMPLETION_ROUTINE, - ) -> BOOL; -} -#[link(name = "ntdll")] -extern "system" { - pub fn NtCreateFile( - filehandle: *mut HANDLE, - desiredaccess: FILE_ACCESS_RIGHTS, - objectattributes: *const OBJECT_ATTRIBUTES, - iostatusblock: *mut IO_STATUS_BLOCK, - allocationsize: *const i64, - fileattributes: FILE_FLAGS_AND_ATTRIBUTES, - shareaccess: FILE_SHARE_MODE, - createdisposition: NTCREATEFILE_CREATE_DISPOSITION, - createoptions: NTCREATEFILE_CREATE_OPTIONS, - eabuffer: *const core::ffi::c_void, - ealength: u32, - ) -> NTSTATUS; -} -#[link(name = "ntdll")] -extern "system" { - pub fn NtReadFile( - filehandle: HANDLE, - event: HANDLE, - apcroutine: PIO_APC_ROUTINE, - apccontext: *const core::ffi::c_void, - iostatusblock: *mut IO_STATUS_BLOCK, - buffer: *mut core::ffi::c_void, - length: u32, - byteoffset: *const i64, - key: *const u32, - ) -> NTSTATUS; -} -#[link(name = "ntdll")] -extern "system" { - pub fn NtWriteFile( - filehandle: HANDLE, - event: HANDLE, - apcroutine: PIO_APC_ROUTINE, - apccontext: *const core::ffi::c_void, - iostatusblock: *mut IO_STATUS_BLOCK, - buffer: *const core::ffi::c_void, - length: u32, - byteoffset: *const i64, - key: *const u32, - ) -> NTSTATUS; -} -#[link(name = "ntdll")] -extern "system" { - pub fn RtlNtStatusToDosError(status: NTSTATUS) -> u32; -} -#[link(name = "userenv")] -extern "system" { - pub fn GetUserProfileDirectoryW( - htoken: HANDLE, - lpprofiledir: PWSTR, - lpcchsize: *mut u32, - ) -> BOOL; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn WSACleanup() -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn WSADuplicateSocketW( - s: SOCKET, - dwprocessid: u32, - lpprotocolinfo: *mut WSAPROTOCOL_INFOW, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn WSAGetLastError() -> WSA_ERROR; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn WSARecv( - s: SOCKET, - lpbuffers: *const WSABUF, - dwbuffercount: u32, - lpnumberofbytesrecvd: *mut u32, - lpflags: *mut u32, - lpoverlapped: *mut OVERLAPPED, - lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn WSASend( - s: SOCKET, - lpbuffers: *const WSABUF, - dwbuffercount: u32, - lpnumberofbytessent: *mut u32, - dwflags: u32, - lpoverlapped: *mut OVERLAPPED, - lpcompletionroutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn WSASocketW( - af: i32, - r#type: i32, - protocol: i32, - lpprotocolinfo: *const WSAPROTOCOL_INFOW, - g: u32, - dwflags: u32, - ) -> SOCKET; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn accept(s: SOCKET, addr: *mut SOCKADDR, addrlen: *mut i32) -> SOCKET; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn bind(s: SOCKET, name: *const SOCKADDR, namelen: i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn closesocket(s: SOCKET) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn connect(s: SOCKET, name: *const SOCKADDR, namelen: i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn freeaddrinfo(paddrinfo: *const ADDRINFOA); -} -#[link(name = "ws2_32")] -extern "system" { - pub fn getaddrinfo( - pnodename: PCSTR, - pservicename: PCSTR, - phints: *const ADDRINFOA, - ppresult: *mut *mut ADDRINFOA, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn getpeername(s: SOCKET, name: *mut SOCKADDR, namelen: *mut i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn getsockname(s: SOCKET, name: *mut SOCKADDR, namelen: *mut i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn getsockopt(s: SOCKET, level: i32, optname: i32, optval: PSTR, optlen: *mut i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn ioctlsocket(s: SOCKET, cmd: i32, argp: *mut u32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn listen(s: SOCKET, backlog: i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn recv(s: SOCKET, buf: PSTR, len: i32, flags: SEND_RECV_FLAGS) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn recvfrom( - s: SOCKET, - buf: PSTR, - len: i32, - flags: i32, - from: *mut SOCKADDR, - fromlen: *mut i32, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn select( - nfds: i32, - readfds: *mut FD_SET, - writefds: *mut FD_SET, - exceptfds: *mut FD_SET, - timeout: *const TIMEVAL, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn send(s: SOCKET, buf: PCSTR, len: i32, flags: SEND_RECV_FLAGS) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn sendto( - s: SOCKET, - buf: PCSTR, - len: i32, - flags: i32, - to: *const SOCKADDR, - tolen: i32, - ) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn setsockopt(s: SOCKET, level: i32, optname: i32, optval: PCSTR, optlen: i32) -> i32; -} -#[link(name = "ws2_32")] -extern "system" { - pub fn shutdown(s: SOCKET, how: WINSOCK_SHUTDOWN_HOW) -> i32; -} +windows_targets::link!("advapi32.dll" "system" fn OpenProcessToken(processhandle : HANDLE, desiredaccess : TOKEN_ACCESS_MASK, tokenhandle : *mut HANDLE) -> BOOL); +windows_targets::link!("advapi32.dll" "system" "SystemFunction036" fn RtlGenRandom(randombuffer : *mut core::ffi::c_void, randombufferlength : u32) -> BOOLEAN); +windows_targets::link!("kernel32.dll" "system" fn AcquireSRWLockExclusive(srwlock : *mut SRWLOCK)); +windows_targets::link!("kernel32.dll" "system" fn AcquireSRWLockShared(srwlock : *mut SRWLOCK)); +windows_targets::link!("kernel32.dll" "system" fn AddVectoredExceptionHandler(first : u32, handler : PVECTORED_EXCEPTION_HANDLER) -> *mut core::ffi::c_void); +windows_targets::link!("kernel32.dll" "system" fn CancelIo(hfile : HANDLE) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn CloseHandle(hobject : HANDLE) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn CompareStringOrdinal(lpstring1 : PCWSTR, cchcount1 : i32, lpstring2 : PCWSTR, cchcount2 : i32, bignorecase : BOOL) -> COMPARESTRING_RESULT); +windows_targets::link!("kernel32.dll" "system" fn CopyFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, lpprogressroutine : LPPROGRESS_ROUTINE, lpdata : *const core::ffi::c_void, pbcancel : *mut BOOL, dwcopyflags : u32) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn CreateDirectoryW(lppathname : PCWSTR, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn CreateEventW(lpeventattributes : *const SECURITY_ATTRIBUTES, bmanualreset : BOOL, binitialstate : BOOL, lpname : PCWSTR) -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn CreateFileW(lpfilename : PCWSTR, dwdesiredaccess : u32, dwsharemode : FILE_SHARE_MODE, lpsecurityattributes : *const SECURITY_ATTRIBUTES, dwcreationdisposition : FILE_CREATION_DISPOSITION, dwflagsandattributes : FILE_FLAGS_AND_ATTRIBUTES, htemplatefile : HANDLE) -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn CreateHardLinkW(lpfilename : PCWSTR, lpexistingfilename : PCWSTR, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn CreateNamedPipeW(lpname : PCWSTR, dwopenmode : FILE_FLAGS_AND_ATTRIBUTES, dwpipemode : NAMED_PIPE_MODE, nmaxinstances : u32, noutbuffersize : u32, ninbuffersize : u32, ndefaulttimeout : u32, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn CreatePipe(hreadpipe : *mut HANDLE, hwritepipe : *mut HANDLE, lppipeattributes : *const SECURITY_ATTRIBUTES, nsize : u32) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn CreateProcessW(lpapplicationname : PCWSTR, lpcommandline : PWSTR, lpprocessattributes : *const SECURITY_ATTRIBUTES, lpthreadattributes : *const SECURITY_ATTRIBUTES, binherithandles : BOOL, dwcreationflags : PROCESS_CREATION_FLAGS, lpenvironment : *const core::ffi::c_void, lpcurrentdirectory : PCWSTR, lpstartupinfo : *const STARTUPINFOW, lpprocessinformation : *mut PROCESS_INFORMATION) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn CreateSymbolicLinkW(lpsymlinkfilename : PCWSTR, lptargetfilename : PCWSTR, dwflags : SYMBOLIC_LINK_FLAGS) -> BOOLEAN); +windows_targets::link!("kernel32.dll" "system" fn CreateThread(lpthreadattributes : *const SECURITY_ATTRIBUTES, dwstacksize : usize, lpstartaddress : LPTHREAD_START_ROUTINE, lpparameter : *const core::ffi::c_void, dwcreationflags : THREAD_CREATION_FLAGS, lpthreadid : *mut u32) -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn CreateWaitableTimerExW(lptimerattributes : *const SECURITY_ATTRIBUTES, lptimername : PCWSTR, dwflags : u32, dwdesiredaccess : u32) -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn DeleteFileW(lpfilename : PCWSTR) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn DeleteProcThreadAttributeList(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST)); +windows_targets::link!("kernel32.dll" "system" fn DeviceIoControl(hdevice : HANDLE, dwiocontrolcode : u32, lpinbuffer : *const core::ffi::c_void, ninbuffersize : u32, lpoutbuffer : *mut core::ffi::c_void, noutbuffersize : u32, lpbytesreturned : *mut u32, lpoverlapped : *mut OVERLAPPED) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn DuplicateHandle(hsourceprocesshandle : HANDLE, hsourcehandle : HANDLE, htargetprocesshandle : HANDLE, lptargethandle : *mut HANDLE, dwdesiredaccess : u32, binherithandle : BOOL, dwoptions : DUPLICATE_HANDLE_OPTIONS) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn ExitProcess(uexitcode : u32) -> !); +windows_targets::link!("kernel32.dll" "system" fn FindClose(hfindfile : HANDLE) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn FindFirstFileW(lpfilename : PCWSTR, lpfindfiledata : *mut WIN32_FIND_DATAW) -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn FindNextFileW(hfindfile : HANDLE, lpfindfiledata : *mut WIN32_FIND_DATAW) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn FlushFileBuffers(hfile : HANDLE) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn FormatMessageW(dwflags : FORMAT_MESSAGE_OPTIONS, lpsource : *const core::ffi::c_void, dwmessageid : u32, dwlanguageid : u32, lpbuffer : PWSTR, nsize : u32, arguments : *const *const i8) -> u32); +windows_targets::link!("kernel32.dll" "system" fn FreeEnvironmentStringsW(penv : PCWSTR) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn GetActiveProcessorCount(groupnumber : u16) -> u32); +windows_targets::link!("kernel32.dll" "system" fn GetCommandLineW() -> PCWSTR); +windows_targets::link!("kernel32.dll" "system" fn GetConsoleMode(hconsolehandle : HANDLE, lpmode : *mut CONSOLE_MODE) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn GetCurrentDirectoryW(nbufferlength : u32, lpbuffer : PWSTR) -> u32); +windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcessId() -> u32); +windows_targets::link!("kernel32.dll" "system" fn GetCurrentThread() -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn GetEnvironmentStringsW() -> PWSTR); +windows_targets::link!("kernel32.dll" "system" fn GetEnvironmentVariableW(lpname : PCWSTR, lpbuffer : PWSTR, nsize : u32) -> u32); +windows_targets::link!("kernel32.dll" "system" fn GetExitCodeProcess(hprocess : HANDLE, lpexitcode : *mut u32) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn GetFileAttributesW(lpfilename : PCWSTR) -> u32); +windows_targets::link!("kernel32.dll" "system" fn GetFileInformationByHandle(hfile : HANDLE, lpfileinformation : *mut BY_HANDLE_FILE_INFORMATION) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn GetFileInformationByHandleEx(hfile : HANDLE, fileinformationclass : FILE_INFO_BY_HANDLE_CLASS, lpfileinformation : *mut core::ffi::c_void, dwbuffersize : u32) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn GetFileType(hfile : HANDLE) -> FILE_TYPE); +windows_targets::link!("kernel32.dll" "system" fn GetFinalPathNameByHandleW(hfile : HANDLE, lpszfilepath : PWSTR, cchfilepath : u32, dwflags : GETFINALPATHNAMEBYHANDLE_FLAGS) -> u32); +windows_targets::link!("kernel32.dll" "system" fn GetFullPathNameW(lpfilename : PCWSTR, nbufferlength : u32, lpbuffer : PWSTR, lpfilepart : *mut PWSTR) -> u32); +windows_targets::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR); +windows_targets::link!("kernel32.dll" "system" fn GetModuleFileNameW(hmodule : HMODULE, lpfilename : PWSTR, nsize : u32) -> u32); +windows_targets::link!("kernel32.dll" "system" fn GetModuleHandleA(lpmodulename : PCSTR) -> HMODULE); +windows_targets::link!("kernel32.dll" "system" fn GetModuleHandleW(lpmodulename : PCWSTR) -> HMODULE); +windows_targets::link!("kernel32.dll" "system" fn GetOverlappedResult(hfile : HANDLE, lpoverlapped : *const OVERLAPPED, lpnumberofbytestransferred : *mut u32, bwait : BOOL) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC); +windows_targets::link!("kernel32.dll" "system" fn GetProcessId(process : HANDLE) -> u32); +windows_targets::link!("kernel32.dll" "system" fn GetStdHandle(nstdhandle : STD_HANDLE) -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn GetSystemDirectoryW(lpbuffer : PWSTR, usize : u32) -> u32); +windows_targets::link!("kernel32.dll" "system" fn GetSystemInfo(lpsysteminfo : *mut SYSTEM_INFO)); +windows_targets::link!("kernel32.dll" "system" fn GetSystemTimeAsFileTime(lpsystemtimeasfiletime : *mut FILETIME)); +windows_targets::link!("kernel32.dll" "system" fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime : *mut FILETIME)); +windows_targets::link!("kernel32.dll" "system" fn GetTempPathW(nbufferlength : u32, lpbuffer : PWSTR) -> u32); +windows_targets::link!("kernel32.dll" "system" fn GetWindowsDirectoryW(lpbuffer : PWSTR, usize : u32) -> u32); +windows_targets::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinitonce : *mut INIT_ONCE, dwflags : u32, fpending : *mut BOOL, lpcontext : *mut *mut core::ffi::c_void) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn InitOnceComplete(lpinitonce : *mut INIT_ONCE, dwflags : u32, lpcontext : *const core::ffi::c_void) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn InitializeProcThreadAttributeList(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST, dwattributecount : u32, dwflags : u32, lpsize : *mut usize) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn LocalFree(hmem : HLOCAL) -> HLOCAL); +windows_targets::link!("kernel32.dll" "system" fn MoveFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, dwflags : MOVE_FILE_FLAGS) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn MultiByteToWideChar(codepage : u32, dwflags : MULTI_BYTE_TO_WIDE_CHAR_FLAGS, lpmultibytestr : PCSTR, cbmultibyte : i32, lpwidecharstr : PWSTR, cchwidechar : i32) -> i32); +windows_targets::link!("kernel32.dll" "system" fn QueryPerformanceCounter(lpperformancecount : *mut i64) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn QueryPerformanceFrequency(lpfrequency : *mut i64) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn ReadConsoleW(hconsoleinput : HANDLE, lpbuffer : *mut core::ffi::c_void, nnumberofcharstoread : u32, lpnumberofcharsread : *mut u32, pinputcontrol : *const CONSOLE_READCONSOLE_CONTROL) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn ReadFile(hfile : HANDLE, lpbuffer : *mut u8, nnumberofbytestoread : u32, lpnumberofbytesread : *mut u32, lpoverlapped : *mut OVERLAPPED) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn ReadFileEx(hfile : HANDLE, lpbuffer : *mut u8, nnumberofbytestoread : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPOVERLAPPED_COMPLETION_ROUTINE) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn ReleaseSRWLockExclusive(srwlock : *mut SRWLOCK)); +windows_targets::link!("kernel32.dll" "system" fn ReleaseSRWLockShared(srwlock : *mut SRWLOCK)); +windows_targets::link!("kernel32.dll" "system" fn RemoveDirectoryW(lppathname : PCWSTR) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn SetCurrentDirectoryW(lppathname : PCWSTR) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn SetEnvironmentVariableW(lpname : PCWSTR, lpvalue : PCWSTR) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn SetFileAttributesW(lpfilename : PCWSTR, dwfileattributes : FILE_FLAGS_AND_ATTRIBUTES) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn SetFileInformationByHandle(hfile : HANDLE, fileinformationclass : FILE_INFO_BY_HANDLE_CLASS, lpfileinformation : *const core::ffi::c_void, dwbuffersize : u32) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn SetFilePointerEx(hfile : HANDLE, lidistancetomove : i64, lpnewfilepointer : *mut i64, dwmovemethod : SET_FILE_POINTER_MOVE_METHOD) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn SetFileTime(hfile : HANDLE, lpcreationtime : *const FILETIME, lplastaccesstime : *const FILETIME, lplastwritetime : *const FILETIME) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn SetHandleInformation(hobject : HANDLE, dwmask : u32, dwflags : HANDLE_FLAGS) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn SetLastError(dwerrcode : WIN32_ERROR)); +windows_targets::link!("kernel32.dll" "system" fn SetThreadStackGuarantee(stacksizeinbytes : *mut u32) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn SetWaitableTimer(htimer : HANDLE, lpduetime : *const i64, lperiod : i32, pfncompletionroutine : PTIMERAPCROUTINE, lpargtocompletionroutine : *const core::ffi::c_void, fresume : BOOL) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn Sleep(dwmilliseconds : u32)); +windows_targets::link!("kernel32.dll" "system" fn SleepConditionVariableSRW(conditionvariable : *mut CONDITION_VARIABLE, srwlock : *mut SRWLOCK, dwmilliseconds : u32, flags : u32) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn SleepEx(dwmilliseconds : u32, balertable : BOOL) -> u32); +windows_targets::link!("kernel32.dll" "system" fn SwitchToThread() -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn TerminateProcess(hprocess : HANDLE, uexitcode : u32) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn TlsAlloc() -> u32); +windows_targets::link!("kernel32.dll" "system" fn TlsFree(dwtlsindex : u32) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn TlsGetValue(dwtlsindex : u32) -> *mut core::ffi::c_void); +windows_targets::link!("kernel32.dll" "system" fn TlsSetValue(dwtlsindex : u32, lptlsvalue : *const core::ffi::c_void) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockExclusive(srwlock : *mut SRWLOCK) -> BOOLEAN); +windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockShared(srwlock : *mut SRWLOCK) -> BOOLEAN); +windows_targets::link!("kernel32.dll" "system" fn UpdateProcThreadAttribute(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST, dwflags : u32, attribute : usize, lpvalue : *const core::ffi::c_void, cbsize : usize, lppreviousvalue : *mut core::ffi::c_void, lpreturnsize : *const usize) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn WaitForMultipleObjects(ncount : u32, lphandles : *const HANDLE, bwaitall : BOOL, dwmilliseconds : u32) -> WAIT_EVENT); +windows_targets::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT); +windows_targets::link!("kernel32.dll" "system" fn WakeAllConditionVariable(conditionvariable : *mut CONDITION_VARIABLE)); +windows_targets::link!("kernel32.dll" "system" fn WakeConditionVariable(conditionvariable : *mut CONDITION_VARIABLE)); +windows_targets::link!("kernel32.dll" "system" fn WideCharToMultiByte(codepage : u32, dwflags : u32, lpwidecharstr : PCWSTR, cchwidechar : i32, lpmultibytestr : PSTR, cbmultibyte : i32, lpdefaultchar : PCSTR, lpuseddefaultchar : *mut BOOL) -> i32); +windows_targets::link!("kernel32.dll" "system" fn WriteConsoleW(hconsoleoutput : HANDLE, lpbuffer : PCWSTR, nnumberofcharstowrite : u32, lpnumberofcharswritten : *mut u32, lpreserved : *const core::ffi::c_void) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn WriteFileEx(hfile : HANDLE, lpbuffer : *const u8, nnumberofbytestowrite : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPOVERLAPPED_COMPLETION_ROUTINE) -> BOOL); +windows_targets::link!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : FILE_ACCESS_RIGHTS, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : FILE_FLAGS_AND_ATTRIBUTES, shareaccess : FILE_SHARE_MODE, createdisposition : NTCREATEFILE_CREATE_DISPOSITION, createoptions : NTCREATEFILE_CREATE_OPTIONS, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS); +windows_targets::link!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); +windows_targets::link!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); +windows_targets::link!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32); +windows_targets::link!("userenv.dll" "system" fn GetUserProfileDirectoryW(htoken : HANDLE, lpprofiledir : PWSTR, lpcchsize : *mut u32) -> BOOL); +windows_targets::link!("ws2_32.dll" "system" fn WSACleanup() -> i32); +windows_targets::link!("ws2_32.dll" "system" fn WSADuplicateSocketW(s : SOCKET, dwprocessid : u32, lpprotocolinfo : *mut WSAPROTOCOL_INFOW) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn WSAGetLastError() -> WSA_ERROR); +windows_targets::link!("ws2_32.dll" "system" fn WSARecv(s : SOCKET, lpbuffers : *const WSABUF, dwbuffercount : u32, lpnumberofbytesrecvd : *mut u32, lpflags : *mut u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn WSASend(s : SOCKET, lpbuffers : *const WSABUF, dwbuffercount : u32, lpnumberofbytessent : *mut u32, dwflags : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn WSASocketW(af : i32, r#type : i32, protocol : i32, lpprotocolinfo : *const WSAPROTOCOL_INFOW, g : u32, dwflags : u32) -> SOCKET); +windows_targets::link!("ws2_32.dll" "system" fn WSAStartup(wversionrequested : u16, lpwsadata : *mut WSADATA) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn accept(s : SOCKET, addr : *mut SOCKADDR, addrlen : *mut i32) -> SOCKET); +windows_targets::link!("ws2_32.dll" "system" fn bind(s : SOCKET, name : *const SOCKADDR, namelen : i32) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn closesocket(s : SOCKET) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn connect(s : SOCKET, name : *const SOCKADDR, namelen : i32) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn freeaddrinfo(paddrinfo : *const ADDRINFOA)); +windows_targets::link!("ws2_32.dll" "system" fn getaddrinfo(pnodename : PCSTR, pservicename : PCSTR, phints : *const ADDRINFOA, ppresult : *mut *mut ADDRINFOA) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn getpeername(s : SOCKET, name : *mut SOCKADDR, namelen : *mut i32) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn getsockname(s : SOCKET, name : *mut SOCKADDR, namelen : *mut i32) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn getsockopt(s : SOCKET, level : i32, optname : i32, optval : PSTR, optlen : *mut i32) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn ioctlsocket(s : SOCKET, cmd : i32, argp : *mut u32) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn listen(s : SOCKET, backlog : i32) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn recv(s : SOCKET, buf : PSTR, len : i32, flags : SEND_RECV_FLAGS) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn recvfrom(s : SOCKET, buf : PSTR, len : i32, flags : i32, from : *mut SOCKADDR, fromlen : *mut i32) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn select(nfds : i32, readfds : *mut FD_SET, writefds : *mut FD_SET, exceptfds : *mut FD_SET, timeout : *const TIMEVAL) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn send(s : SOCKET, buf : PCSTR, len : i32, flags : SEND_RECV_FLAGS) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn sendto(s : SOCKET, buf : PCSTR, len : i32, flags : i32, to : *const SOCKADDR, tolen : i32) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn setsockopt(s : SOCKET, level : i32, optname : i32, optval : PCSTR, optlen : i32) -> i32); +windows_targets::link!("ws2_32.dll" "system" fn shutdown(s : SOCKET, how : WINSOCK_SHUTDOWN_HOW) -> i32); pub const ABOVE_NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 32768u32; pub type ADDRESS_FAMILY = u16; #[repr(C)] +#[derive(Clone, Copy)] pub struct ADDRINFOA { pub ai_flags: i32, pub ai_family: i32, @@ -851,18 +148,13 @@ pub struct ADDRINFOA { pub ai_addr: *mut SOCKADDR, pub ai_next: *mut ADDRINFOA, } -impl Copy for ADDRINFOA {} -impl Clone for ADDRINFOA { - fn clone(&self) -> Self { - *self - } -} pub const AF_INET: ADDRESS_FAMILY = 2u16; pub const AF_INET6: ADDRESS_FAMILY = 23u16; pub const AF_UNIX: u16 = 1u16; pub const AF_UNSPEC: ADDRESS_FAMILY = 0u16; pub const ALL_PROCESSOR_GROUPS: u16 = 65535u16; #[repr(C)] +#[derive(Clone, Copy)] pub union ARM64_NT_NEON128 { pub Anonymous: ARM64_NT_NEON128_0, pub D: [f64; 2], @@ -870,27 +162,17 @@ pub union ARM64_NT_NEON128 { pub H: [u16; 8], pub B: [u8; 16], } -impl Copy for ARM64_NT_NEON128 {} -impl Clone for ARM64_NT_NEON128 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct ARM64_NT_NEON128_0 { pub Low: u64, pub High: i64, } -impl Copy for ARM64_NT_NEON128_0 {} -impl Clone for ARM64_NT_NEON128_0 { - fn clone(&self) -> Self { - *self - } -} pub const BELOW_NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 16384u32; pub type BOOL = i32; pub type BOOLEAN = u8; #[repr(C)] +#[derive(Clone, Copy)] pub struct BY_HANDLE_FILE_INFORMATION { pub dwFileAttributes: u32, pub ftCreationTime: FILETIME, @@ -903,41 +185,26 @@ pub struct BY_HANDLE_FILE_INFORMATION { pub nFileIndexHigh: u32, pub nFileIndexLow: u32, } -impl Copy for BY_HANDLE_FILE_INFORMATION {} -impl Clone for BY_HANDLE_FILE_INFORMATION { - fn clone(&self) -> Self { - *self - } -} pub const CALLBACK_CHUNK_FINISHED: LPPROGRESS_ROUTINE_CALLBACK_REASON = 0u32; pub const CALLBACK_STREAM_SWITCH: LPPROGRESS_ROUTINE_CALLBACK_REASON = 1u32; pub type COMPARESTRING_RESULT = i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct CONDITION_VARIABLE { pub Ptr: *mut core::ffi::c_void, } -impl Copy for CONDITION_VARIABLE {} -impl Clone for CONDITION_VARIABLE { - fn clone(&self) -> Self { - *self - } -} pub type CONSOLE_MODE = u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct CONSOLE_READCONSOLE_CONTROL { pub nLength: u32, pub nInitialChars: u32, pub dwCtrlWakeupMask: u32, pub dwControlKeyState: u32, } -impl Copy for CONSOLE_READCONSOLE_CONTROL {} -impl Clone for CONSOLE_READCONSOLE_CONTROL { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "aarch64")] +#[derive(Clone, Copy)] pub struct CONTEXT { pub ContextFlags: CONTEXT_FLAGS, pub Cpsr: u32, @@ -952,30 +219,16 @@ pub struct CONTEXT { pub Wcr: [u32; 2], pub Wvr: [u64; 2], } -#[cfg(target_arch = "aarch64")] -impl Copy for CONTEXT {} -#[cfg(target_arch = "aarch64")] -impl Clone for CONTEXT { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "aarch64")] +#[derive(Clone, Copy)] pub union CONTEXT_0 { pub Anonymous: CONTEXT_0_0, pub X: [u64; 31], } -#[cfg(target_arch = "aarch64")] -impl Copy for CONTEXT_0 {} -#[cfg(target_arch = "aarch64")] -impl Clone for CONTEXT_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "aarch64")] +#[derive(Clone, Copy)] pub struct CONTEXT_0_0 { pub X0: u64, pub X1: u64, @@ -1009,16 +262,9 @@ pub struct CONTEXT_0_0 { pub Fp: u64, pub Lr: u64, } -#[cfg(target_arch = "aarch64")] -impl Copy for CONTEXT_0_0 {} -#[cfg(target_arch = "aarch64")] -impl Clone for CONTEXT_0_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] pub struct CONTEXT { pub P1Home: u64, pub P2Home: u64, @@ -1067,30 +313,16 @@ pub struct CONTEXT { pub LastExceptionToRip: u64, pub LastExceptionFromRip: u64, } -#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] -impl Copy for CONTEXT {} -#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] -impl Clone for CONTEXT { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] pub union CONTEXT_0 { pub FltSave: XSAVE_FORMAT, pub Anonymous: CONTEXT_0_0, } -#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] -impl Copy for CONTEXT_0 {} -#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] -impl Clone for CONTEXT_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] pub struct CONTEXT_0_0 { pub Header: [M128A; 2], pub Legacy: [M128A; 8], @@ -1111,16 +343,9 @@ pub struct CONTEXT_0_0 { pub Xmm14: M128A, pub Xmm15: M128A, } -#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] -impl Copy for CONTEXT_0_0 {} -#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] -impl Clone for CONTEXT_0_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "x86")] +#[derive(Clone, Copy)] pub struct CONTEXT { pub ContextFlags: CONTEXT_FLAGS, pub Dr0: u32, @@ -1148,14 +373,6 @@ pub struct CONTEXT { pub SegSs: u32, pub ExtendedRegisters: [u8; 512], } -#[cfg(target_arch = "x86")] -impl Copy for CONTEXT {} -#[cfg(target_arch = "x86")] -impl Clone for CONTEXT { - fn clone(&self) -> Self { - *self - } -} pub type CONTEXT_FLAGS = u32; pub const CP_UTF8: u32 = 65001u32; pub const CREATE_ALWAYS: FILE_CREATION_DISPOSITION = 2u32; @@ -3068,6 +2285,13 @@ pub const ERROR_XML_PARSE_ERROR: WIN32_ERROR = 1465u32; pub type EXCEPTION_DISPOSITION = i32; pub const EXCEPTION_MAXIMUM_PARAMETERS: u32 = 15u32; #[repr(C)] +#[derive(Clone, Copy)] +pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: *mut CONTEXT, +} +#[repr(C)] +#[derive(Clone, Copy)] pub struct EXCEPTION_RECORD { pub ExceptionCode: NTSTATUS, pub ExceptionFlags: u32, @@ -3076,12 +2300,6 @@ pub struct EXCEPTION_RECORD { pub NumberParameters: u32, pub ExceptionInformation: [usize; 15], } -impl Copy for EXCEPTION_RECORD {} -impl Clone for EXCEPTION_RECORD { - fn clone(&self) -> Self { - *self - } -} pub const EXCEPTION_STACK_OVERFLOW: NTSTATUS = 0xC00000FD_u32 as _; pub const EXTENDED_STARTUPINFO_PRESENT: PROCESS_CREATION_FLAGS = 524288u32; pub const E_NOTIMPL: HRESULT = 0x80004001_u32 as _; @@ -3095,40 +2313,25 @@ pub const FALSE: BOOL = 0i32; pub type FARPROC = Option isize>; pub const FAST_FAIL_FATAL_APP_EXIT: u32 = 7u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FD_SET { pub fd_count: u32, pub fd_array: [SOCKET; 64], } -impl Copy for FD_SET {} -impl Clone for FD_SET { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct FILETIME { pub dwLowDateTime: u32, pub dwHighDateTime: u32, } -impl Copy for FILETIME {} -impl Clone for FILETIME { - fn clone(&self) -> Self { - *self - } -} pub type FILE_ACCESS_RIGHTS = u32; pub const FILE_ADD_FILE: FILE_ACCESS_RIGHTS = 2u32; pub const FILE_ADD_SUBDIRECTORY: FILE_ACCESS_RIGHTS = 4u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_ALLOCATION_INFO { pub AllocationSize: i64, } -impl Copy for FILE_ALLOCATION_INFO {} -impl Clone for FILE_ALLOCATION_INFO { - fn clone(&self) -> Self { - *self - } -} pub const FILE_ALL_ACCESS: FILE_ACCESS_RIGHTS = 2032127u32; pub const FILE_APPEND_DATA: FILE_ACCESS_RIGHTS = 4u32; pub const FILE_ATTRIBUTE_ARCHIVE: FILE_FLAGS_AND_ATTRIBUTES = 32u32; @@ -3151,20 +2354,16 @@ pub const FILE_ATTRIBUTE_REPARSE_POINT: FILE_FLAGS_AND_ATTRIBUTES = 1024u32; pub const FILE_ATTRIBUTE_SPARSE_FILE: FILE_FLAGS_AND_ATTRIBUTES = 512u32; pub const FILE_ATTRIBUTE_SYSTEM: FILE_FLAGS_AND_ATTRIBUTES = 4u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_ATTRIBUTE_TAG_INFO { pub FileAttributes: u32, pub ReparseTag: u32, } -impl Copy for FILE_ATTRIBUTE_TAG_INFO {} -impl Clone for FILE_ATTRIBUTE_TAG_INFO { - fn clone(&self) -> Self { - *self - } -} pub const FILE_ATTRIBUTE_TEMPORARY: FILE_FLAGS_AND_ATTRIBUTES = 256u32; pub const FILE_ATTRIBUTE_UNPINNED: FILE_FLAGS_AND_ATTRIBUTES = 1048576u32; pub const FILE_ATTRIBUTE_VIRTUAL: FILE_FLAGS_AND_ATTRIBUTES = 65536u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_BASIC_INFO { pub CreationTime: i64, pub LastAccessTime: i64, @@ -3172,12 +2371,6 @@ pub struct FILE_BASIC_INFO { pub ChangeTime: i64, pub FileAttributes: u32, } -impl Copy for FILE_BASIC_INFO {} -impl Clone for FILE_BASIC_INFO { - fn clone(&self) -> Self { - *self - } -} pub const FILE_BEGIN: SET_FILE_POINTER_MOVE_METHOD = 0u32; pub const FILE_COMPLETE_IF_OPLOCKED: NTCREATEFILE_CREATE_OPTIONS = 256u32; pub const FILE_CONTAINS_EXTENDED_CREATE_INFORMATION: NTCREATEFILE_CREATE_OPTIONS = 268435456u32; @@ -3197,37 +2390,22 @@ pub const FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE: FILE_DISPOSITION_INFO pub const FILE_DISPOSITION_FLAG_ON_CLOSE: FILE_DISPOSITION_INFO_EX_FLAGS = 8u32; pub const FILE_DISPOSITION_FLAG_POSIX_SEMANTICS: FILE_DISPOSITION_INFO_EX_FLAGS = 2u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_DISPOSITION_INFO { pub DeleteFile: BOOLEAN, } -impl Copy for FILE_DISPOSITION_INFO {} -impl Clone for FILE_DISPOSITION_INFO { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_DISPOSITION_INFO_EX { pub Flags: FILE_DISPOSITION_INFO_EX_FLAGS, } -impl Copy for FILE_DISPOSITION_INFO_EX {} -impl Clone for FILE_DISPOSITION_INFO_EX { - fn clone(&self) -> Self { - *self - } -} pub type FILE_DISPOSITION_INFO_EX_FLAGS = u32; pub const FILE_END: SET_FILE_POINTER_MOVE_METHOD = 2u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_END_OF_FILE_INFO { pub EndOfFile: i64, } -impl Copy for FILE_END_OF_FILE_INFO {} -impl Clone for FILE_END_OF_FILE_INFO { - fn clone(&self) -> Self { - *self - } -} pub const FILE_EXECUTE: FILE_ACCESS_RIGHTS = 32u32; pub type FILE_FLAGS_AND_ATTRIBUTES = u32; pub const FILE_FLAG_BACKUP_SEMANTICS: FILE_FLAGS_AND_ATTRIBUTES = 33554432u32; @@ -3246,6 +2424,7 @@ pub const FILE_GENERIC_EXECUTE: FILE_ACCESS_RIGHTS = 1179808u32; pub const FILE_GENERIC_READ: FILE_ACCESS_RIGHTS = 1179785u32; pub const FILE_GENERIC_WRITE: FILE_ACCESS_RIGHTS = 1179926u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_ID_BOTH_DIR_INFO { pub NextEntryOffset: u32, pub FileIndex: u32, @@ -3263,23 +2442,12 @@ pub struct FILE_ID_BOTH_DIR_INFO { pub FileId: i64, pub FileName: [u16; 1], } -impl Copy for FILE_ID_BOTH_DIR_INFO {} -impl Clone for FILE_ID_BOTH_DIR_INFO { - fn clone(&self) -> Self { - *self - } -} pub type FILE_INFO_BY_HANDLE_CLASS = i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_IO_PRIORITY_HINT_INFO { pub PriorityHint: PRIORITY_HINT, } -impl Copy for FILE_IO_PRIORITY_HINT_INFO {} -impl Clone for FILE_IO_PRIORITY_HINT_INFO { - fn clone(&self) -> Self { - *self - } -} pub const FILE_LIST_DIRECTORY: FILE_ACCESS_RIGHTS = 1u32; pub const FILE_NAME_NORMALIZED: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32; pub const FILE_NAME_OPENED: GETFINALPATHNAMEBYHANDLE_FLAGS = 8u32; @@ -3310,6 +2478,7 @@ pub const FILE_SHARE_NONE: FILE_SHARE_MODE = 0u32; pub const FILE_SHARE_READ: FILE_SHARE_MODE = 1u32; pub const FILE_SHARE_WRITE: FILE_SHARE_MODE = 2u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct FILE_STANDARD_INFO { pub AllocationSize: i64, pub EndOfFile: i64, @@ -3317,12 +2486,6 @@ pub struct FILE_STANDARD_INFO { pub DeletePending: BOOLEAN, pub Directory: BOOLEAN, } -impl Copy for FILE_STANDARD_INFO {} -impl Clone for FILE_STANDARD_INFO { - fn clone(&self) -> Self { - *self - } -} pub const FILE_SUPERSEDE: NTCREATEFILE_CREATE_DISPOSITION = 0u32; pub const FILE_SYNCHRONOUS_IO_ALERT: NTCREATEFILE_CREATE_OPTIONS = 16u32; pub const FILE_SYNCHRONOUS_IO_NONALERT: NTCREATEFILE_CREATE_OPTIONS = 32u32; @@ -3340,6 +2503,7 @@ pub const FILE_WRITE_THROUGH: NTCREATEFILE_CREATE_OPTIONS = 2u32; pub const FIONBIO: i32 = -2147195266i32; #[repr(C)] #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] pub struct FLOATING_SAVE_AREA { pub ControlWord: u32, pub StatusWord: u32, @@ -3351,16 +2515,9 @@ pub struct FLOATING_SAVE_AREA { pub RegisterArea: [u8; 80], pub Cr0NpxState: u32, } -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] -impl Copy for FLOATING_SAVE_AREA {} -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] -impl Clone for FLOATING_SAVE_AREA { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "x86")] +#[derive(Clone, Copy)] pub struct FLOATING_SAVE_AREA { pub ControlWord: u32, pub StatusWord: u32, @@ -3372,14 +2529,6 @@ pub struct FLOATING_SAVE_AREA { pub RegisterArea: [u8; 80], pub Spare0: u32, } -#[cfg(target_arch = "x86")] -impl Copy for FLOATING_SAVE_AREA {} -#[cfg(target_arch = "x86")] -impl Clone for FLOATING_SAVE_AREA { - fn clone(&self) -> Self { - *self - } -} pub const FORMAT_MESSAGE_ALLOCATE_BUFFER: FORMAT_MESSAGE_OPTIONS = 256u32; pub const FORMAT_MESSAGE_ARGUMENT_ARRAY: FORMAT_MESSAGE_OPTIONS = 8192u32; pub const FORMAT_MESSAGE_FROM_HMODULE: FORMAT_MESSAGE_OPTIONS = 2048u32; @@ -3422,18 +2571,13 @@ pub const GENERIC_READ: GENERIC_ACCESS_RIGHTS = 2147483648u32; pub const GENERIC_WRITE: GENERIC_ACCESS_RIGHTS = 1073741824u32; pub type GETFINALPATHNAMEBYHANDLE_FLAGS = u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct GUID { pub data1: u32, pub data2: u16, pub data3: u16, pub data4: [u8; 8], } -impl Copy for GUID {} -impl Clone for GUID { - fn clone(&self) -> Self { - *self - } -} impl GUID { pub const fn from_u128(uuid: u128) -> Self { Self { @@ -3454,112 +2598,67 @@ pub type HMODULE = *mut core::ffi::c_void; pub type HRESULT = i32; pub const IDLE_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 64u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct IN6_ADDR { pub u: IN6_ADDR_0, } -impl Copy for IN6_ADDR {} -impl Clone for IN6_ADDR { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub union IN6_ADDR_0 { pub Byte: [u8; 16], pub Word: [u16; 8], } -impl Copy for IN6_ADDR_0 {} -impl Clone for IN6_ADDR_0 { - fn clone(&self) -> Self { - *self - } -} pub const INFINITE: u32 = 4294967295u32; pub const INHERIT_CALLER_PRIORITY: PROCESS_CREATION_FLAGS = 131072u32; pub const INHERIT_PARENT_AFFINITY: PROCESS_CREATION_FLAGS = 65536u32; #[repr(C)] +#[derive(Clone, Copy)] pub union INIT_ONCE { pub Ptr: *mut core::ffi::c_void, } -impl Copy for INIT_ONCE {} -impl Clone for INIT_ONCE { - fn clone(&self) -> Self { - *self - } -} pub const INIT_ONCE_INIT_FAILED: u32 = 4u32; pub const INVALID_FILE_ATTRIBUTES: u32 = 4294967295u32; pub const INVALID_SOCKET: SOCKET = -1i32 as _; #[repr(C)] +#[derive(Clone, Copy)] pub struct IN_ADDR { pub S_un: IN_ADDR_0, } -impl Copy for IN_ADDR {} -impl Clone for IN_ADDR { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub union IN_ADDR_0 { pub S_un_b: IN_ADDR_0_0, pub S_un_w: IN_ADDR_0_1, pub S_addr: u32, } -impl Copy for IN_ADDR_0 {} -impl Clone for IN_ADDR_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct IN_ADDR_0_0 { pub s_b1: u8, pub s_b2: u8, pub s_b3: u8, pub s_b4: u8, } -impl Copy for IN_ADDR_0_0 {} -impl Clone for IN_ADDR_0_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct IN_ADDR_0_1 { pub s_w1: u16, pub s_w2: u16, } -impl Copy for IN_ADDR_0_1 {} -impl Clone for IN_ADDR_0_1 { - fn clone(&self) -> Self { - *self - } -} pub const IO_REPARSE_TAG_MOUNT_POINT: u32 = 2684354563u32; pub const IO_REPARSE_TAG_SYMLINK: u32 = 2684354572u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct IO_STATUS_BLOCK { pub Anonymous: IO_STATUS_BLOCK_0, pub Information: usize, } -impl Copy for IO_STATUS_BLOCK {} -impl Clone for IO_STATUS_BLOCK { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub union IO_STATUS_BLOCK_0 { pub Status: NTSTATUS, pub Pointer: *mut core::ffi::c_void, } -impl Copy for IO_STATUS_BLOCK_0 {} -impl Clone for IO_STATUS_BLOCK_0 { - fn clone(&self) -> Self { - *self - } -} pub type IPPROTO = i32; pub const IPPROTO_AH: IPPROTO = 51i32; pub const IPPROTO_CBT: IPPROTO = 7i32; @@ -3601,45 +2700,30 @@ pub const IPPROTO_UDP: IPPROTO = 17i32; pub const IPV6_ADD_MEMBERSHIP: i32 = 12i32; pub const IPV6_DROP_MEMBERSHIP: i32 = 13i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct IPV6_MREQ { pub ipv6mr_multiaddr: IN6_ADDR, pub ipv6mr_interface: u32, } -impl Copy for IPV6_MREQ {} -impl Clone for IPV6_MREQ { - fn clone(&self) -> Self { - *self - } -} pub const IPV6_MULTICAST_LOOP: i32 = 11i32; pub const IPV6_V6ONLY: i32 = 27i32; pub const IP_ADD_MEMBERSHIP: i32 = 12i32; pub const IP_DROP_MEMBERSHIP: i32 = 13i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct IP_MREQ { pub imr_multiaddr: IN_ADDR, pub imr_interface: IN_ADDR, } -impl Copy for IP_MREQ {} -impl Clone for IP_MREQ { - fn clone(&self) -> Self { - *self - } -} pub const IP_MULTICAST_LOOP: i32 = 11i32; pub const IP_MULTICAST_TTL: i32 = 10i32; pub const IP_TTL: i32 = 4i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct LINGER { pub l_onoff: u16, pub l_linger: u16, } -impl Copy for LINGER {} -impl Clone for LINGER { - fn clone(&self) -> Self { - *self - } -} pub type LPOVERLAPPED_COMPLETION_ROUTINE = Option< unsafe extern "system" fn( dwerrorcode: u32, @@ -3673,16 +2757,11 @@ pub type LPWSAOVERLAPPED_COMPLETION_ROUTINE = Option< ), >; #[repr(C)] +#[derive(Clone, Copy)] pub struct M128A { pub Low: u64, pub High: i64, } -impl Copy for M128A {} -impl Clone for M128A { - fn clone(&self) -> Self { - *self - } -} pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: u32 = 16384u32; pub const MAX_PATH: u32 = 260u32; pub const MB_COMPOSITE: MULTI_BYTE_TO_WIDE_CHAR_FLAGS = 2u32; @@ -3710,6 +2789,7 @@ pub type NTCREATEFILE_CREATE_DISPOSITION = u32; pub type NTCREATEFILE_CREATE_OPTIONS = u32; pub type NTSTATUS = i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct OBJECT_ATTRIBUTES { pub Length: u32, pub RootDirectory: HANDLE, @@ -3718,50 +2798,29 @@ pub struct OBJECT_ATTRIBUTES { pub SecurityDescriptor: *const core::ffi::c_void, pub SecurityQualityOfService: *const core::ffi::c_void, } -impl Copy for OBJECT_ATTRIBUTES {} -impl Clone for OBJECT_ATTRIBUTES { - fn clone(&self) -> Self { - *self - } -} pub const OBJ_DONT_REPARSE: i32 = 4096i32; pub const OPEN_ALWAYS: FILE_CREATION_DISPOSITION = 4u32; pub const OPEN_EXISTING: FILE_CREATION_DISPOSITION = 3u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct OVERLAPPED { pub Internal: usize, pub InternalHigh: usize, pub Anonymous: OVERLAPPED_0, pub hEvent: HANDLE, } -impl Copy for OVERLAPPED {} -impl Clone for OVERLAPPED { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub union OVERLAPPED_0 { pub Anonymous: OVERLAPPED_0_0, pub Pointer: *mut core::ffi::c_void, } -impl Copy for OVERLAPPED_0 {} -impl Clone for OVERLAPPED_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct OVERLAPPED_0_0 { pub Offset: u32, pub OffsetHigh: u32, } -impl Copy for OVERLAPPED_0_0 {} -impl Clone for OVERLAPPED_0_0 { - fn clone(&self) -> Self { - *self - } -} pub type PCSTR = *const u8; pub type PCWSTR = *const u16; pub type PIO_APC_ROUTINE = Option< @@ -3788,18 +2847,13 @@ pub type PRIORITY_HINT = i32; pub type PROCESSOR_ARCHITECTURE = u16; pub type PROCESS_CREATION_FLAGS = u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct PROCESS_INFORMATION { pub hProcess: HANDLE, pub hThread: HANDLE, pub dwProcessId: u32, pub dwThreadId: u32, } -impl Copy for PROCESS_INFORMATION {} -impl Clone for PROCESS_INFORMATION { - fn clone(&self) -> Self { - *self - } -} pub const PROCESS_MODE_BACKGROUND_BEGIN: PROCESS_CREATION_FLAGS = 1048576u32; pub const PROCESS_MODE_BACKGROUND_END: PROCESS_CREATION_FLAGS = 2097152u32; pub const PROFILE_KERNEL: PROCESS_CREATION_FLAGS = 536870912u32; @@ -3814,6 +2868,8 @@ pub type PTIMERAPCROUTINE = Option< dwtimerhighvalue: u32, ), >; +pub type PVECTORED_EXCEPTION_HANDLER = + Option i32>; pub type PWSTR = *mut u16; pub const READ_CONTROL: FILE_ACCESS_RIGHTS = 131072u32; pub const REALTIME_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 256u32; @@ -3822,17 +2878,12 @@ pub const SD_RECEIVE: WINSOCK_SHUTDOWN_HOW = 0i32; pub const SD_SEND: WINSOCK_SHUTDOWN_HOW = 1i32; pub const SECURITY_ANONYMOUS: FILE_FLAGS_AND_ATTRIBUTES = 0u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct SECURITY_ATTRIBUTES { pub nLength: u32, pub lpSecurityDescriptor: *mut core::ffi::c_void, pub bInheritHandle: BOOL, } -impl Copy for SECURITY_ATTRIBUTES {} -impl Clone for SECURITY_ATTRIBUTES { - fn clone(&self) -> Self { - *self - } -} pub const SECURITY_CONTEXT_TRACKING: FILE_FLAGS_AND_ATTRIBUTES = 262144u32; pub const SECURITY_DELEGATION: FILE_FLAGS_AND_ATTRIBUTES = 196608u32; pub const SECURITY_EFFECTIVE_ONLY: FILE_FLAGS_AND_ATTRIBUTES = 524288u32; @@ -3843,27 +2894,25 @@ pub const SECURITY_VALID_SQOS_FLAGS: FILE_FLAGS_AND_ATTRIBUTES = 2031616u32; pub type SEND_RECV_FLAGS = i32; pub type SET_FILE_POINTER_MOVE_METHOD = u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct SOCKADDR { pub sa_family: ADDRESS_FAMILY, pub sa_data: [i8; 14], } -impl Copy for SOCKADDR {} -impl Clone for SOCKADDR { - fn clone(&self) -> Self { - *self - } +#[repr(C)] +#[derive(Clone, Copy)] +pub struct SOCKADDR_STORAGE { + pub ss_family: ADDRESS_FAMILY, + pub __ss_pad1: [i8; 6], + pub __ss_align: i64, + pub __ss_pad2: [i8; 112], } #[repr(C)] +#[derive(Clone, Copy)] pub struct SOCKADDR_UN { pub sun_family: ADDRESS_FAMILY, pub sun_path: [i8; 108], } -impl Copy for SOCKADDR_UN {} -impl Clone for SOCKADDR_UN { - fn clone(&self) -> Self { - *self - } -} pub type SOCKET = usize; pub const SOCKET_ERROR: i32 = -1i32; pub const SOCK_DGRAM: WINSOCK_SOCKET_TYPE = 2i32; @@ -3879,15 +2928,10 @@ pub const SO_RCVTIMEO: i32 = 4102i32; pub const SO_SNDTIMEO: i32 = 4101i32; pub const SPECIFIC_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 65535u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct SRWLOCK { pub Ptr: *mut core::ffi::c_void, } -impl Copy for SRWLOCK {} -impl Clone for SRWLOCK { - fn clone(&self) -> Self { - *self - } -} pub const STACK_SIZE_PARAM_IS_A_RESERVATION: THREAD_CREATION_FLAGS = 65536u32; pub const STANDARD_RIGHTS_ALL: FILE_ACCESS_RIGHTS = 2031616u32; pub const STANDARD_RIGHTS_EXECUTE: FILE_ACCESS_RIGHTS = 131072u32; @@ -3909,17 +2953,13 @@ pub const STARTF_USESHOWWINDOW: STARTUPINFOW_FLAGS = 1u32; pub const STARTF_USESIZE: STARTUPINFOW_FLAGS = 2u32; pub const STARTF_USESTDHANDLES: STARTUPINFOW_FLAGS = 256u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct STARTUPINFOEXW { pub StartupInfo: STARTUPINFOW, pub lpAttributeList: LPPROC_THREAD_ATTRIBUTE_LIST, } -impl Copy for STARTUPINFOEXW {} -impl Clone for STARTUPINFOEXW { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct STARTUPINFOW { pub cb: u32, pub lpReserved: PWSTR, @@ -3940,12 +2980,6 @@ pub struct STARTUPINFOW { pub hStdOutput: HANDLE, pub hStdError: HANDLE, } -impl Copy for STARTUPINFOW {} -impl Clone for STARTUPINFOW { - fn clone(&self) -> Self { - *self - } -} pub type STARTUPINFOW_FLAGS = u32; pub const STATUS_DELETE_PENDING: NTSTATUS = 0xC0000056_u32 as _; pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _; @@ -3964,6 +2998,7 @@ pub const SYMLINK_FLAG_RELATIVE: u32 = 1u32; pub type SYNCHRONIZATION_ACCESS_RIGHTS = u32; pub const SYNCHRONIZE: FILE_ACCESS_RIGHTS = 1048576u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct SYSTEM_INFO { pub Anonymous: SYSTEM_INFO_0, pub dwPageSize: u32, @@ -3976,34 +3011,18 @@ pub struct SYSTEM_INFO { pub wProcessorLevel: u16, pub wProcessorRevision: u16, } -impl Copy for SYSTEM_INFO {} -impl Clone for SYSTEM_INFO { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub union SYSTEM_INFO_0 { pub dwOemId: u32, pub Anonymous: SYSTEM_INFO_0_0, } -impl Copy for SYSTEM_INFO_0 {} -impl Clone for SYSTEM_INFO_0 { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct SYSTEM_INFO_0_0 { pub wProcessorArchitecture: PROCESSOR_ARCHITECTURE, pub wReserved: u16, } -impl Copy for SYSTEM_INFO_0_0 {} -impl Clone for SYSTEM_INFO_0_0 { - fn clone(&self) -> Self { - *self - } -} pub const TCP_NODELAY: i32 = 1i32; pub const THREAD_CREATE_RUN_IMMEDIATELY: THREAD_CREATION_FLAGS = 0u32; pub const THREAD_CREATE_SUSPENDED: THREAD_CREATION_FLAGS = 4u32; @@ -4011,16 +3030,11 @@ pub type THREAD_CREATION_FLAGS = u32; pub const TIMER_ALL_ACCESS: SYNCHRONIZATION_ACCESS_RIGHTS = 2031619u32; pub const TIMER_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct TIMEVAL { pub tv_sec: i32, pub tv_usec: i32, } -impl Copy for TIMEVAL {} -impl Clone for TIMEVAL { - fn clone(&self) -> Self { - *self - } -} pub const TLS_OUT_OF_INDEXES: u32 = 4294967295u32; pub type TOKEN_ACCESS_MASK = u32; pub const TOKEN_ACCESS_PSEUDO_HANDLE: TOKEN_ACCESS_MASK = 24u32; @@ -4047,17 +3061,12 @@ pub const TOKEN_WRITE_OWNER: TOKEN_ACCESS_MASK = 524288u32; pub const TRUE: BOOL = 1i32; pub const TRUNCATE_EXISTING: FILE_CREATION_DISPOSITION = 5u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct UNICODE_STRING { pub Length: u16, pub MaximumLength: u16, pub Buffer: PWSTR, } -impl Copy for UNICODE_STRING {} -impl Clone for UNICODE_STRING { - fn clone(&self) -> Self { - *self - } -} pub const VOLUME_NAME_DOS: GETFINALPATHNAMEBYHANDLE_FLAGS = 0u32; pub const VOLUME_NAME_GUID: GETFINALPATHNAMEBYHANDLE_FLAGS = 1u32; pub const VOLUME_NAME_NONE: GETFINALPATHNAMEBYHANDLE_FLAGS = 4u32; @@ -4071,6 +3080,7 @@ pub const WAIT_TIMEOUT: WAIT_EVENT = 258u32; pub const WC_ERR_INVALID_CHARS: u32 = 128u32; pub type WIN32_ERROR = u32; #[repr(C)] +#[derive(Clone, Copy)] pub struct WIN32_FIND_DATAW { pub dwFileAttributes: u32, pub ftCreationTime: FILETIME, @@ -4083,30 +3093,20 @@ pub struct WIN32_FIND_DATAW { pub cFileName: [u16; 260], pub cAlternateFileName: [u16; 14], } -impl Copy for WIN32_FIND_DATAW {} -impl Clone for WIN32_FIND_DATAW { - fn clone(&self) -> Self { - *self - } -} pub type WINSOCK_SHUTDOWN_HOW = i32; pub type WINSOCK_SOCKET_TYPE = i32; pub const WRITE_DAC: FILE_ACCESS_RIGHTS = 262144u32; pub const WRITE_OWNER: FILE_ACCESS_RIGHTS = 524288u32; pub const WSABASEERR: WSA_ERROR = 10000i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct WSABUF { pub len: u32, pub buf: PSTR, } -impl Copy for WSABUF {} -impl Clone for WSABUF { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] pub struct WSADATA { pub wVersion: u16, pub wHighVersion: u16, @@ -4116,16 +3116,9 @@ pub struct WSADATA { pub szDescription: [i8; 257], pub szSystemStatus: [i8; 129], } -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] -impl Copy for WSADATA {} -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] -impl Clone for WSADATA { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "x86")] +#[derive(Clone, Copy)] pub struct WSADATA { pub wVersion: u16, pub wHighVersion: u16, @@ -4135,14 +3128,6 @@ pub struct WSADATA { pub iMaxUdpDg: u16, pub lpVendorInfo: PSTR, } -#[cfg(target_arch = "x86")] -impl Copy for WSADATA {} -#[cfg(target_arch = "x86")] -impl Clone for WSADATA { - fn clone(&self) -> Self { - *self - } -} pub const WSAEACCES: WSA_ERROR = 10013i32; pub const WSAEADDRINUSE: WSA_ERROR = 10048i32; pub const WSAEADDRNOTAVAIL: WSA_ERROR = 10049i32; @@ -4198,17 +3183,13 @@ pub const WSANOTINITIALISED: WSA_ERROR = 10093i32; pub const WSANO_DATA: WSA_ERROR = 11004i32; pub const WSANO_RECOVERY: WSA_ERROR = 11003i32; #[repr(C)] +#[derive(Clone, Copy)] pub struct WSAPROTOCOLCHAIN { pub ChainLen: i32, pub ChainEntries: [u32; 7], } -impl Copy for WSAPROTOCOLCHAIN {} -impl Clone for WSAPROTOCOLCHAIN { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] +#[derive(Clone, Copy)] pub struct WSAPROTOCOL_INFOW { pub dwServiceFlags1: u32, pub dwServiceFlags2: u32, @@ -4231,12 +3212,6 @@ pub struct WSAPROTOCOL_INFOW { pub dwProviderReserved: u32, pub szProtocol: [u16; 256], } -impl Copy for WSAPROTOCOL_INFOW {} -impl Clone for WSAPROTOCOL_INFOW { - fn clone(&self) -> Self { - *self - } -} pub const WSASERVICE_NOT_FOUND: WSA_ERROR = 10108i32; pub const WSASYSCALLFAILURE: WSA_ERROR = 10107i32; pub const WSASYSNOTREADY: WSA_ERROR = 10091i32; @@ -4287,6 +3262,7 @@ pub const WSA_WAIT_EVENT_0: WSA_ERROR = 0i32; pub const WSA_WAIT_IO_COMPLETION: WSA_ERROR = 192i32; #[repr(C)] #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] +#[derive(Clone, Copy)] pub struct XSAVE_FORMAT { pub ControlWord: u16, pub StatusWord: u16, @@ -4305,16 +3281,9 @@ pub struct XSAVE_FORMAT { pub XmmRegisters: [M128A; 16], pub Reserved4: [u8; 96], } -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] -impl Copy for XSAVE_FORMAT {} -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] -impl Clone for XSAVE_FORMAT { - fn clone(&self) -> Self { - *self - } -} #[repr(C)] #[cfg(target_arch = "x86")] +#[derive(Clone, Copy)] pub struct XSAVE_FORMAT { pub ControlWord: u16, pub StatusWord: u16, @@ -4333,12 +3302,19 @@ pub struct XSAVE_FORMAT { pub XmmRegisters: [M128A; 8], pub Reserved4: [u8; 224], } -#[cfg(target_arch = "x86")] -impl Copy for XSAVE_FORMAT {} -#[cfg(target_arch = "x86")] -impl Clone for XSAVE_FORMAT { - fn clone(&self) -> Self { - *self - } + +#[cfg(target_arch = "arm")] +#[repr(C)] +pub struct WSADATA { + pub wVersion: u16, + pub wHighVersion: u16, + pub szDescription: [u8; 257], + pub szSystemStatus: [u8; 129], + pub iMaxSockets: u16, + pub iMaxUdpDg: u16, + pub lpVendorInfo: PSTR, } +#[cfg(target_arch = "arm")] +pub enum CONTEXT {} // ignore-tidy-filelength +use super::windows_targets; diff --git a/library/std/src/sys/pal/windows/c/windows_targets.rs b/library/std/src/sys/pal/windows/c/windows_targets.rs new file mode 100644 index 0000000000000..252bceb70942b --- /dev/null +++ b/library/std/src/sys/pal/windows/c/windows_targets.rs @@ -0,0 +1,37 @@ +//! Provides the `link!` macro used by the generated windows bindings. +//! +//! This is a simple wrapper around an `extern` block with a `#[link]` attribute. +//! It's very roughly equivalent to the windows-targets crate. + +#[cfg(feature = "windows_raw_dylib")] +pub macro link { + ($library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => ( + #[cfg_attr(not(target_arch = "x86"), link(name = $library, kind = "raw-dylib", modifiers = "+verbatim"))] + #[cfg_attr(target_arch = "x86", link(name = $library, kind = "raw-dylib", modifiers = "+verbatim", import_name_type = "undecorated"))] + extern $abi { + $(#[link_name=$link_name])? + pub fn $($function)*; + } + ) +} +#[cfg(not(feature = "windows_raw_dylib"))] +pub macro link { + ($library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => ( + // Note: the windows-targets crate uses a pre-built Windows.lib import library which we don't + // have in this repo. So instead we always link kernel32.lib and add the rest of the import + // libraries below by using an empty extern block. This works because extern blocks are not + // connected to the library given in the #[link] attribute. + #[link(name = "kernel32")] + extern $abi { + $(#[link_name=$link_name])? + pub fn $($function)*; + } + ) +} + +#[cfg(not(feature = "windows_raw_dylib"))] +#[link(name = "advapi32")] +#[link(name = "ntdll")] +#[link(name = "userenv")] +#[link(name = "ws2_32")] +extern "C" {} diff --git a/library/std/src/sys/pal/windows/compat.rs b/library/std/src/sys/pal/windows/compat.rs index f5d57a28db69a..75232dfc0b0f1 100644 --- a/library/std/src/sys/pal/windows/compat.rs +++ b/library/std/src/sys/pal/windows/compat.rs @@ -112,8 +112,10 @@ impl Module { /// (e.g. kernel32 and ntdll). pub unsafe fn new(name: &CStr) -> Option { // SAFETY: A CStr is always null terminated. - let module = c::GetModuleHandleA(name.as_ptr().cast::()); - NonNull::new(module).map(Self) + unsafe { + let module = c::GetModuleHandleA(name.as_ptr().cast::()); + NonNull::new(module).map(Self) + } } // Try to get the address of a function. @@ -156,8 +158,10 @@ macro_rules! compat_fn_with_fallback { static PTR: AtomicPtr = AtomicPtr::new(load as *mut _); unsafe extern "system" fn load($($argname: $argtype),*) -> $rettype { - let func = load_from_module(Module::new($module)); - func($($argname),*) + unsafe { + let func = load_from_module(Module::new($module)); + func($($argname),*) + } } fn load_from_module(module: Option) -> F { @@ -180,8 +184,10 @@ macro_rules! compat_fn_with_fallback { #[inline(always)] pub unsafe fn call($($argname: $argtype),*) -> $rettype { - let func: F = mem::transmute(PTR.load(Ordering::Relaxed)); - func($($argname),*) + unsafe { + let func: F = mem::transmute(PTR.load(Ordering::Relaxed)); + func($($argname),*) + } } } #[allow(unused)] @@ -223,7 +229,7 @@ macro_rules! compat_fn_optional { } #[inline] pub unsafe extern "system" fn $symbol($($argname: $argtype),*) $(-> $rettype)? { - $symbol::option().unwrap()($($argname),*) + unsafe { $symbol::option().unwrap()($($argname),*) } } )+ ) diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index e92c5e80eac9c..d99d4931de40f 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -1,25 +1,21 @@ use core::ptr::addr_of; -use crate::os::windows::prelude::*; - +use super::api::{self, WinError}; +use super::{to_u16s, IoResult}; use crate::borrow::Cow; use crate::ffi::{c_void, OsStr, OsString}; -use crate::fmt; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem::{self, MaybeUninit}; use crate::os::windows::io::{AsHandle, BorrowedHandle}; +use crate::os::windows::prelude::*; use crate::path::{Path, PathBuf}; -use crate::ptr; -use crate::slice; use crate::sync::Arc; use crate::sys::handle::Handle; +use crate::sys::path::maybe_verbatim; use crate::sys::time::SystemTime; use crate::sys::{c, cvt, Align8}; use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::thread; - -use super::{api, to_u16s, IoResult}; -use crate::sys::path::maybe_verbatim; +use crate::{fmt, ptr, slice, thread}; pub struct File { handle: Handle, @@ -27,12 +23,13 @@ pub struct File { #[derive(Clone)] pub struct FileAttr { - attributes: c::DWORD, + attributes: u32, creation_time: c::FILETIME, last_access_time: c::FILETIME, last_write_time: c::FILETIME, + change_time: Option, file_size: u64, - reparse_tag: c::DWORD, + reparse_tag: u32, volume_serial_number: Option, number_of_links: Option, file_index: Option, @@ -40,8 +37,8 @@ pub struct FileAttr { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct FileType { - attributes: c::DWORD, - reparse_tag: c::DWORD, + attributes: u32, + reparse_tag: u32, } pub struct ReadDir { @@ -74,16 +71,16 @@ pub struct OpenOptions { create_new: bool, // system-specific custom_flags: u32, - access_mode: Option, - attributes: c::DWORD, - share_mode: c::DWORD, - security_qos_flags: c::DWORD, - security_attributes: c::LPSECURITY_ATTRIBUTES, + access_mode: Option, + attributes: u32, + share_mode: u32, + security_qos_flags: u32, + security_attributes: *mut c::SECURITY_ATTRIBUTES, } #[derive(Clone, PartialEq, Eq, Debug)] pub struct FilePermissions { - attrs: c::DWORD, + attrs: u32, } #[derive(Copy, Clone, Debug, Default)] @@ -130,10 +127,11 @@ impl Iterator for ReadDir { let mut wfd = mem::zeroed(); loop { if c::FindNextFileW(self.handle.0, &mut wfd) == 0 { - if api::get_last_error().code == c::ERROR_NO_MORE_FILES { - return None; - } else { - return Some(Err(Error::last_os_error())); + match api::get_last_error() { + WinError::NO_MORE_FILES => return None, + WinError { code } => { + return Some(Err(Error::from_raw_os_error(code as i32))); + } } } if let Some(e) = DirEntry::new(&self.root, &wfd) { @@ -239,13 +237,11 @@ impl OpenOptions { // receive is `SECURITY_ANONYMOUS = 0x0`, which we can't check for later on. self.security_qos_flags = flags | c::SECURITY_SQOS_PRESENT; } - pub fn security_attributes(&mut self, attrs: c::LPSECURITY_ATTRIBUTES) { + pub fn security_attributes(&mut self, attrs: *mut c::SECURITY_ATTRIBUTES) { self.security_attributes = attrs; } - fn get_access_mode(&self) -> io::Result { - const ERROR_INVALID_PARAMETER: i32 = 87; - + fn get_access_mode(&self) -> io::Result { match (self.read, self.write, self.append, self.access_mode) { (.., Some(mode)) => Ok(mode), (true, false, false, None) => Ok(c::GENERIC_READ), @@ -255,23 +251,23 @@ impl OpenOptions { (true, _, true, None) => { Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA)) } - (false, false, false, None) => Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)), + (false, false, false, None) => { + Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)) + } } } - fn get_creation_mode(&self) -> io::Result { - const ERROR_INVALID_PARAMETER: i32 = 87; - + fn get_creation_mode(&self) -> io::Result { match (self.write, self.append) { (true, false) => {} (false, false) => { if self.truncate || self.create || self.create_new { - return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); + return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)); } } (_, true) => { if self.truncate && !self.create_new { - return Err(Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); + return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32)); } } } @@ -287,7 +283,7 @@ impl OpenOptions { }) } - fn get_flags_and_attributes(&self) -> c::DWORD { + fn get_flags_and_attributes(&self) -> u32 { self.custom_flags | self.attributes | self.security_qos_flags @@ -315,7 +311,7 @@ impl File { // Manual truncation. See #115745. if opts.truncate && creation == c::OPEN_ALWAYS - && unsafe { c::GetLastError() } == c::ERROR_ALREADY_EXISTS + && api::get_last_error() == WinError::ALREADY_EXISTS { unsafe { // This originally used `FileAllocationInfo` instead of @@ -377,6 +373,7 @@ impl File { creation_time: info.ftCreationTime, last_access_time: info.ftLastAccessTime, last_write_time: info.ftLastWriteTime, + change_time: None, // Only available in FILE_BASIC_INFO file_size: (info.nFileSizeLow as u64) | ((info.nFileSizeHigh as u64) << 32), reparse_tag, volume_serial_number: Some(info.dwVolumeSerialNumber), @@ -397,22 +394,26 @@ impl File { self.handle.as_raw_handle(), c::FileBasicInfo, core::ptr::addr_of_mut!(info) as *mut c_void, - size as c::DWORD, + size as u32, ))?; let mut attr = FileAttr { attributes: info.FileAttributes, creation_time: c::FILETIME { - dwLowDateTime: info.CreationTime as c::DWORD, - dwHighDateTime: (info.CreationTime >> 32) as c::DWORD, + dwLowDateTime: info.CreationTime as u32, + dwHighDateTime: (info.CreationTime >> 32) as u32, }, last_access_time: c::FILETIME { - dwLowDateTime: info.LastAccessTime as c::DWORD, - dwHighDateTime: (info.LastAccessTime >> 32) as c::DWORD, + dwLowDateTime: info.LastAccessTime as u32, + dwHighDateTime: (info.LastAccessTime >> 32) as u32, }, last_write_time: c::FILETIME { - dwLowDateTime: info.LastWriteTime as c::DWORD, - dwHighDateTime: (info.LastWriteTime >> 32) as c::DWORD, + dwLowDateTime: info.LastWriteTime as u32, + dwHighDateTime: (info.LastWriteTime >> 32) as u32, }, + change_time: Some(c::FILETIME { + dwLowDateTime: info.ChangeTime as u32, + dwHighDateTime: (info.ChangeTime >> 32) as u32, + }), file_size: 0, reparse_tag: 0, volume_serial_number: None, @@ -425,7 +426,7 @@ impl File { self.handle.as_raw_handle(), c::FileStandardInfo, core::ptr::addr_of_mut!(info) as *mut c_void, - size as c::DWORD, + size as u32, ))?; attr.file_size = info.AllocationSize as u64; attr.number_of_links = Some(info.NumberOfLinks); @@ -495,7 +496,7 @@ impl File { SeekFrom::End(n) => (c::FILE_END, n), SeekFrom::Current(n) => (c::FILE_CURRENT, n), }; - let pos = pos as c::LARGE_INTEGER; + let pos = pos as i64; let mut newpos = 0; cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?; Ok(newpos as u64) @@ -511,7 +512,7 @@ impl File { fn reparse_point( &self, space: &mut Align8<[MaybeUninit]>, - ) -> io::Result<(c::DWORD, *mut c::REPARSE_DATA_BUFFER)> { + ) -> io::Result<(u32, *mut c::REPARSE_DATA_BUFFER)> { unsafe { let mut bytes = 0; cvt({ @@ -524,7 +525,7 @@ impl File { ptr::null_mut(), 0, space.0.as_mut_ptr().cast(), - len as c::DWORD, + len as u32, &mut bytes, ptr::null_mut(), ) @@ -609,8 +610,7 @@ impl File { "Cannot set file timestamp to 0", )); } - let is_max = - |t: c::FILETIME| t.dwLowDateTime == c::DWORD::MAX && t.dwHighDateTime == c::DWORD::MAX; + let is_max = |t: c::FILETIME| t.dwLowDateTime == u32::MAX && t.dwHighDateTime == u32::MAX; if times.accessed.map_or(false, is_max) || times.modified.map_or(false, is_max) || times.created.map_or(false, is_max) @@ -632,7 +632,7 @@ impl File { Ok(()) } - /// Get only basic file information such as attributes and file times. + /// Gets only basic file information such as attributes and file times. fn basic_info(&self) -> io::Result { unsafe { let mut info: c::FILE_BASIC_INFO = mem::zeroed(); @@ -641,7 +641,7 @@ impl File { self.handle.as_raw_handle(), c::FileBasicInfo, core::ptr::addr_of_mut!(info) as *mut c_void, - size as c::DWORD, + size as u32, ))?; Ok(info) } @@ -795,10 +795,12 @@ impl<'a> Iterator for DirBuffIter<'a> { } unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> { - if p.is_aligned() { - Cow::Borrowed(crate::slice::from_raw_parts(p, len)) - } else { - Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect()) + unsafe { + if p.is_aligned() { + Cow::Borrowed(crate::slice::from_raw_parts(p, len)) + } else { + Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect()) + } } } @@ -845,7 +847,7 @@ fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result< // We make a special exception for `STATUS_DELETE_PENDING` because // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is // very unhelpful. - Err(io::Error::from_raw_os_error(c::ERROR_DELETE_PENDING as _)) + Err(io::Error::from_raw_os_error(c::ERROR_DELETE_PENDING as i32)) } else if status == c::STATUS_INVALID_PARAMETER && ATTRIBUTES.load(Ordering::Relaxed) == c::OBJ_DONT_REPARSE { @@ -897,7 +899,9 @@ impl IntoRawHandle for File { impl FromRawHandle for File { unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { - Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)) } + unsafe { + Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)) } + } } } @@ -954,6 +958,10 @@ impl FileAttr { to_u64(&self.creation_time) } + pub fn changed_u64(&self) -> Option { + self.change_time.as_ref().map(|c| to_u64(c)) + } + pub fn volume_serial_number(&self) -> Option { self.volume_serial_number } @@ -973,6 +981,7 @@ impl From for FileAttr { creation_time: wfd.ftCreationTime, last_access_time: wfd.ftLastAccessTime, last_write_time: wfd.ftLastWriteTime, + change_time: None, file_size: ((wfd.nFileSizeHigh as u64) << 32) | (wfd.nFileSizeLow as u64), reparse_tag: if wfd.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { // reserved unless this is a reparse point @@ -1020,7 +1029,7 @@ impl FileTimes { } impl FileType { - fn new(attrs: c::DWORD, reparse_tag: c::DWORD) -> FileType { + fn new(attrs: u32, reparse_tag: u32) -> FileType { FileType { attributes: attrs, reparse_tag } } pub fn is_dir(&self) -> bool { @@ -1097,7 +1106,7 @@ pub fn readdir(p: &Path) -> io::Result { // // See issue #120040: https://github.com/rust-lang/rust/issues/120040. let last_error = api::get_last_error(); - if last_error.code == c::ERROR_FILE_NOT_FOUND { + if last_error == WinError::FILE_NOT_FOUND { return Ok(ReadDir { handle: FindNextFileHandle(find_handle), root: Arc::new(root), @@ -1417,20 +1426,22 @@ pub fn canonicalize(p: &Path) -> io::Result { pub fn copy(from: &Path, to: &Path) -> io::Result { unsafe extern "system" fn callback( - _TotalFileSize: c::LARGE_INTEGER, - _TotalBytesTransferred: c::LARGE_INTEGER, - _StreamSize: c::LARGE_INTEGER, - StreamBytesTransferred: c::LARGE_INTEGER, - dwStreamNumber: c::DWORD, - _dwCallbackReason: c::DWORD, + _TotalFileSize: i64, + _TotalBytesTransferred: i64, + _StreamSize: i64, + StreamBytesTransferred: i64, + dwStreamNumber: u32, + _dwCallbackReason: u32, _hSourceFile: c::HANDLE, _hDestinationFile: c::HANDLE, - lpData: c::LPCVOID, - ) -> c::DWORD { - if dwStreamNumber == 1 { - *(lpData as *mut i64) = StreamBytesTransferred; + lpData: *const c_void, + ) -> u32 { + unsafe { + if dwStreamNumber == 1 { + *(lpData as *mut i64) = StreamBytesTransferred; + } + c::PROGRESS_CONTINUE } - c::PROGRESS_CONTINUE } let pfrom = maybe_verbatim(from)?; let pto = maybe_verbatim(to)?; @@ -1531,7 +1542,7 @@ pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> { } // Try to see if a file exists but, unlike `exists`, report I/O errors. -pub fn try_exists(path: &Path) -> io::Result { +pub fn exists(path: &Path) -> io::Result { // Open the file to ensure any symlinks are followed to their target. let mut opts = OpenOptions::new(); // No read, write, etc access rights are needed. diff --git a/library/std/src/sys/pal/windows/futex.rs b/library/std/src/sys/pal/windows/futex.rs index bc19c402d9c12..8c5081a607aa3 100644 --- a/library/std/src/sys/pal/windows/futex.rs +++ b/library/std/src/sys/pal/windows/futex.rs @@ -1,15 +1,20 @@ -use super::api; -use crate::sys::c; -use crate::sys::dur2timeout; use core::ffi::c_void; -use core::mem; -use core::ptr; use core::sync::atomic::{ AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize, }; use core::time::Duration; +use core::{mem, ptr}; +use super::api::{self, WinError}; +use crate::sys::{c, dur2timeout}; + +/// An atomic for use as a futex that is at least 8-bits but may be larger. +pub type SmallAtomic = AtomicU8; +/// Must be the underlying type of SmallAtomic +pub type SmallPrimitive = u8; + +pub unsafe trait Futex {} pub unsafe trait Waitable { type Atomic; } @@ -19,6 +24,7 @@ macro_rules! unsafe_waitable_int { unsafe impl Waitable for $int { type Atomic = $atomic; } + unsafe impl Futex for $atomic {} )* }; } @@ -41,6 +47,7 @@ unsafe impl Waitable for *const T { unsafe impl Waitable for *mut T { type Atomic = AtomicPtr; } +unsafe impl Futex for AtomicPtr {} pub fn wait_on_address( address: &W::Atomic, @@ -56,14 +63,14 @@ pub fn wait_on_address( } } -pub fn wake_by_address_single(address: &T) { +pub fn wake_by_address_single(address: &T) { unsafe { let addr = ptr::from_ref(address).cast::(); c::WakeByAddressSingle(addr); } } -pub fn wake_by_address_all(address: &T) { +pub fn wake_by_address_all(address: &T) { unsafe { let addr = ptr::from_ref(address).cast::(); c::WakeByAddressAll(addr); @@ -72,14 +79,14 @@ pub fn wake_by_address_all(address: &T) { pub fn futex_wait(futex: &W::Atomic, expected: W, timeout: Option) -> bool { // return false only on timeout - wait_on_address(futex, expected, timeout) || api::get_last_error().code != c::ERROR_TIMEOUT + wait_on_address(futex, expected, timeout) || api::get_last_error() != WinError::TIMEOUT } -pub fn futex_wake(futex: &T) -> bool { +pub fn futex_wake(futex: &T) -> bool { wake_by_address_single(futex); false } -pub fn futex_wake_all(futex: &T) { +pub fn futex_wake_all(futex: &T) { wake_by_address_all(futex) } diff --git a/library/std/src/sys/pal/windows/handle.rs b/library/std/src/sys/pal/windows/handle.rs index 3f85bb0a099a9..82a880faf5fa7 100644 --- a/library/std/src/sys/pal/windows/handle.rs +++ b/library/std/src/sys/pal/windows/handle.rs @@ -3,20 +3,20 @@ #[cfg(test)] mod tests; -use crate::cmp; +use core::ffi::c_void; +use core::{cmp, mem, ptr}; + use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, Read}; -use crate::mem; use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, }; -use crate::ptr; -use crate::sys::c; -use crate::sys::cvt; +use crate::sys::{c, cvt}; use crate::sys_common::{AsInner, FromInner, IntoInner}; /// An owned container for `HANDLE` object, closing them on Drop. /// /// All methods are inherited through a `Deref` impl to `RawHandle` +#[derive(Debug)] pub struct Handle(OwnedHandle); impl Handle { @@ -72,7 +72,7 @@ impl IntoRawHandle for Handle { impl FromRawHandle for Handle { unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { - Self(FromRawHandle::from_raw_handle(raw_handle)) + unsafe { Self(FromRawHandle::from_raw_handle(raw_handle)) } } } @@ -136,15 +136,31 @@ impl Handle { } } + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { + let mut me = self; + + Read::read_to_end(&mut me, buf) + } + pub unsafe fn read_overlapped( &self, - buf: &mut [u8], + buf: &mut [mem::MaybeUninit], overlapped: *mut c::OVERLAPPED, ) -> io::Result> { - let len = cmp::min(buf.len(), ::MAX as usize) as c::DWORD; - let mut amt = 0; - let res = - cvt(c::ReadFile(self.as_raw_handle(), buf.as_mut_ptr(), len, &mut amt, overlapped)); + // SAFETY: We have exclusive access to the buffer and it's up to the caller to + // ensure the OVERLAPPED pointer is valid for the lifetime of this function. + let (res, amt) = unsafe { + let len = cmp::min(buf.len(), u32::MAX as usize) as u32; + let mut amt = 0; + let res = cvt(c::ReadFile( + self.as_raw_handle(), + buf.as_mut_ptr().cast::(), + len, + &mut amt, + overlapped, + )); + (res, amt) + }; match res { Ok(_) => Ok(Some(amt as usize)), Err(e) => { @@ -209,12 +225,7 @@ impl Handle { Ok(Self(self.0.try_clone()?)) } - pub fn duplicate( - &self, - access: c::DWORD, - inherit: bool, - options: c::DWORD, - ) -> io::Result { + pub fn duplicate(&self, access: u32, inherit: bool, options: u32) -> io::Result { Ok(Self(self.0.as_handle().duplicate(access, inherit, options)?)) } @@ -233,21 +244,25 @@ impl Handle { let mut io_status = c::IO_STATUS_BLOCK::PENDING; // The length is clamped at u32::MAX. - let len = cmp::min(len, c::DWORD::MAX as usize) as c::DWORD; - let status = c::NtReadFile( - self.as_handle(), - ptr::null_mut(), - None, - ptr::null_mut(), - &mut io_status, - buf, - len, - offset.map(|n| n as _).as_ref(), - None, - ); + let len = cmp::min(len, u32::MAX as usize) as u32; + // SAFETY: It's up to the caller to ensure `buf` is writeable up to + // the provided `len`. + let status = unsafe { + c::NtReadFile( + self.as_raw_handle(), + ptr::null_mut(), + None, + ptr::null_mut(), + &mut io_status, + buf.cast::(), + len, + offset.as_ref().map(|n| ptr::from_ref(n).cast::()).unwrap_or(ptr::null()), + ptr::null(), + ) + }; let status = if status == c::STATUS_PENDING { - c::WaitForSingleObject(self.as_raw_handle(), c::INFINITE); + unsafe { c::WaitForSingleObject(self.as_raw_handle(), c::INFINITE) }; io_status.status() } else { status @@ -265,7 +280,7 @@ impl Handle { status if c::nt_success(status) => Ok(io_status.Information), status => { - let error = c::RtlNtStatusToDosError(status); + let error = unsafe { c::RtlNtStatusToDosError(status) }; Err(io::Error::from_raw_os_error(error as _)) } } @@ -281,18 +296,18 @@ impl Handle { let mut io_status = c::IO_STATUS_BLOCK::PENDING; // The length is clamped at u32::MAX. - let len = cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD; + let len = cmp::min(buf.len(), u32::MAX as usize) as u32; let status = unsafe { c::NtWriteFile( - self.as_handle(), + self.as_raw_handle(), ptr::null_mut(), None, ptr::null_mut(), &mut io_status, - buf.as_ptr(), + buf.as_ptr().cast::(), len, - offset.map(|n| n as _).as_ref(), - None, + offset.as_ref().map(|n| ptr::from_ref(n).cast::()).unwrap_or(ptr::null()), + ptr::null(), ) }; let status = if status == c::STATUS_PENDING { diff --git a/library/std/src/sys/pal/windows/io.rs b/library/std/src/sys/pal/windows/io.rs index 77b8f3c410eb8..785a3f6768b70 100644 --- a/library/std/src/sys/pal/windows/io.rs +++ b/library/std/src/sys/pal/windows/io.rs @@ -1,9 +1,10 @@ +use core::ffi::c_void; + use crate::marker::PhantomData; use crate::mem::size_of; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; use crate::slice; use crate::sys::c; -use core::ffi::c_void; #[derive(Copy, Clone)] #[repr(transparent)] @@ -15,9 +16,9 @@ pub struct IoSlice<'a> { impl<'a> IoSlice<'a> { #[inline] pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - assert!(buf.len() <= c::ULONG::MAX as usize); + assert!(buf.len() <= u32::MAX as usize); IoSlice { - vec: c::WSABUF { len: buf.len() as c::ULONG, buf: buf.as_ptr() as *mut u8 }, + vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 }, _p: PhantomData, } } @@ -29,7 +30,7 @@ impl<'a> IoSlice<'a> { } unsafe { - self.vec.len -= n as c::ULONG; + self.vec.len -= n as u32; self.vec.buf = self.vec.buf.add(n); } } @@ -49,9 +50,9 @@ pub struct IoSliceMut<'a> { impl<'a> IoSliceMut<'a> { #[inline] pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - assert!(buf.len() <= c::ULONG::MAX as usize); + assert!(buf.len() <= u32::MAX as usize); IoSliceMut { - vec: c::WSABUF { len: buf.len() as c::ULONG, buf: buf.as_mut_ptr() }, + vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() }, _p: PhantomData, } } @@ -63,7 +64,7 @@ impl<'a> IoSliceMut<'a> { } unsafe { - self.vec.len -= n as c::ULONG; + self.vec.len -= n as u32; self.vec.buf = self.vec.buf.add(n); } } @@ -80,19 +81,17 @@ impl<'a> IoSliceMut<'a> { } pub fn is_terminal(h: &impl AsHandle) -> bool { - unsafe { handle_is_console(h.as_handle()) } + handle_is_console(h.as_handle()) } -unsafe fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { - let handle = handle.as_raw_handle(); - +fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { // A null handle means the process has no console. - if handle.is_null() { + if handle.as_raw_handle().is_null() { return false; } let mut out = 0; - if c::GetConsoleMode(handle, &mut out) != 0 { + if unsafe { c::GetConsoleMode(handle.as_raw_handle(), &mut out) != 0 } { // False positives aren't possible. If we got a console then we definitely have a console. return true; } @@ -101,9 +100,9 @@ unsafe fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { msys_tty_on(handle) } -unsafe fn msys_tty_on(handle: c::HANDLE) -> bool { +fn msys_tty_on(handle: BorrowedHandle<'_>) -> bool { // Early return if the handle is not a pipe. - if c::GetFileType(handle) != c::FILE_TYPE_PIPE { + if unsafe { c::GetFileType(handle.as_raw_handle()) != c::FILE_TYPE_PIPE } { return false; } @@ -119,12 +118,14 @@ unsafe fn msys_tty_on(handle: c::HANDLE) -> bool { } let mut name_info = FILE_NAME_INFO { FileNameLength: 0, FileName: [0; c::MAX_PATH as usize] }; // Safety: buffer length is fixed. - let res = c::GetFileInformationByHandleEx( - handle, - c::FileNameInfo, - core::ptr::addr_of_mut!(name_info) as *mut c_void, - size_of::() as u32, - ); + let res = unsafe { + c::GetFileInformationByHandleEx( + handle.as_raw_handle(), + c::FileNameInfo, + core::ptr::addr_of_mut!(name_info) as *mut c_void, + size_of::() as u32, + ) + }; if res == 0 { return false; } diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 402a205977b07..6ed77fbc3d445 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -1,5 +1,7 @@ #![allow(missing_docs, nonstandard_style)] +#![forbid(unsafe_op_in_unsafe_fn)] +pub use self::rand::hashmap_random_keys; use crate::ffi::{OsStr, OsString}; use crate::io::ErrorKind; use crate::mem::MaybeUninit; @@ -8,8 +10,6 @@ use crate::path::PathBuf; use crate::sys::pal::windows::api::wide_str; use crate::time::Duration; -pub use self::rand::hashmap_random_keys; - #[macro_use] pub mod compat; @@ -31,8 +31,6 @@ pub mod process; pub mod rand; pub mod stdio; pub mod thread; -pub mod thread_local_dtor; -pub mod thread_local_key; pub mod time; cfg_if::cfg_if! { if #[cfg(not(target_vendor = "uwp"))] { @@ -56,11 +54,13 @@ impl IoResult for Result { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { - stack_overflow::init(); + unsafe { + stack_overflow::init(); - // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already - // exists, we have to call it ourselves. - thread::Thread::set_name_wide(wide_str!("main")); + // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already + // exists, we have to call it ourselves. + thread::Thread::set_name_wide(wide_str!("main")); + } } // SAFETY: must be called only once during runtime cleanup. @@ -77,7 +77,7 @@ pub fn is_interrupted(_errno: i32) -> bool { pub fn decode_error_kind(errno: i32) -> ErrorKind { use ErrorKind::*; - match errno as c::DWORD { + match errno as u32 { c::ERROR_ACCESS_DENIED => return PermissionDenied, c::ERROR_ALREADY_EXISTS => return AlreadyExists, c::ERROR_FILE_EXISTS => return AlreadyExists, @@ -218,7 +218,7 @@ pub fn to_u16s>(s: S) -> crate::io::Result> { // from this closure is then the return value of the function. pub fn fill_utf16_buf(mut f1: F1, f2: F2) -> crate::io::Result where - F1: FnMut(*mut u16, c::DWORD) -> c::DWORD, + F1: FnMut(*mut u16, u32) -> u32, F2: FnOnce(&[u16]) -> T, { // Start off with a stack buf but then spill over to the heap if we end up @@ -227,7 +227,7 @@ where // This initial size also works around `GetFullPathNameW` returning // incorrect size hints for some short paths: // https://github.com/dylni/normpath/issues/5 - let mut stack_buf: [MaybeUninit; 512] = MaybeUninit::uninit_array(); + let mut stack_buf: [MaybeUninit; 512] = [MaybeUninit::uninit(); 512]; let mut heap_buf: Vec> = Vec::new(); unsafe { let mut n = stack_buf.len(); @@ -240,7 +240,7 @@ where // We used `reserve` and not `reserve_exact`, so in theory we // may have gotten more than requested. If so, we'd like to use // it... so long as we won't cause overflow. - n = heap_buf.capacity().min(c::DWORD::MAX as usize); + n = heap_buf.capacity().min(u32::MAX as usize); // Safety: MaybeUninit does not need initialization heap_buf.set_len(n); &mut heap_buf[..] @@ -256,13 +256,13 @@ where // error" is still 0 then we interpret it as a 0 length buffer and // not an actual error. c::SetLastError(0); - let k = match f1(buf.as_mut_ptr().cast::(), n as c::DWORD) { + let k = match f1(buf.as_mut_ptr().cast::(), n as u32) { 0 if api::get_last_error().code == 0 => 0, 0 => return Err(crate::io::Error::last_os_error()), n => n, } as usize; if k == n && api::get_last_error().code == c::ERROR_INSUFFICIENT_BUFFER { - n = n.saturating_mul(2).min(c::DWORD::MAX as usize); + n = n.saturating_mul(2).min(u32::MAX as usize); } else if k > n { n = k; } else if k == n { @@ -310,7 +310,7 @@ pub fn cvt(i: I) -> crate::io::Result { if i.is_zero() { Err(crate::io::Error::last_os_error()) } else { Ok(i) } } -pub fn dur2timeout(dur: Duration) -> c::DWORD { +pub fn dur2timeout(dur: Duration) -> u32 { // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the // timeouts in windows APIs are typically u32 milliseconds. To translate, we // have two pieces to take care of: @@ -322,7 +322,7 @@ pub fn dur2timeout(dur: Duration) -> c::DWORD { .checked_mul(1000) .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000)) .and_then(|ms| ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 { 1 } else { 0 })) - .map(|ms| if ms > ::MAX as u64 { c::INFINITE } else { ms as c::DWORD }) + .map(|ms| if ms > ::MAX as u64 { c::INFINITE } else { ms as u32 }) .unwrap_or(c::INFINITE) } diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs index 9e15b15a3513a..ce995f5ed5af7 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/pal/windows/net.rs @@ -1,30 +1,113 @@ #![unstable(issue = "none", feature = "windows_net")] -use crate::cmp; +use core::ffi::{c_int, c_long, c_ulong, c_ushort}; + use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut, Read}; -use crate::mem; use crate::net::{Shutdown, SocketAddr}; use crate::os::windows::io::{ AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, }; -use crate::ptr; use crate::sync::OnceLock; -use crate::sys; use crate::sys::c; -use crate::sys_common::net; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys_common::{net, AsInner, FromInner, IntoInner}; use crate::time::Duration; +use crate::{cmp, mem, ptr, sys}; -use core::ffi::{c_int, c_long, c_ulong, c_ushort}; - +#[allow(non_camel_case_types)] pub type wrlen_t = i32; pub mod netc { - pub use crate::sys::c::ADDRESS_FAMILY as sa_family_t; - pub use crate::sys::c::ADDRINFOA as addrinfo; - pub use crate::sys::c::SOCKADDR as sockaddr; - pub use crate::sys::c::SOCKADDR_STORAGE_LH as sockaddr_storage; - pub use crate::sys::c::*; + //! BSD socket compatibility shim + //! + //! Some Windows API types are not quite what's expected by our cross-platform + //! net code. E.g. naming differences or different pointer types. + + use core::ffi::{c_char, c_int, c_uint, c_ulong, c_ushort, c_void}; + + use crate::sys::c::{self, ADDRESS_FAMILY, ADDRINFOA, SOCKADDR, SOCKET}; + // re-exports from Windows API bindings. + pub use crate::sys::c::{ + bind, connect, freeaddrinfo, getpeername, getsockname, getsockopt, listen, setsockopt, + ADDRESS_FAMILY as sa_family_t, ADDRINFOA as addrinfo, IPPROTO_IP, IPPROTO_IPV6, + IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, + IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, + SOCKADDR as sockaddr, SOCKADDR_STORAGE as sockaddr_storage, SOCK_DGRAM, SOCK_STREAM, + SOL_SOCKET, SO_BROADCAST, SO_RCVTIMEO, SO_SNDTIMEO, + }; + + #[allow(non_camel_case_types)] + pub type socklen_t = c_int; + + pub const AF_INET: i32 = c::AF_INET as i32; + pub const AF_INET6: i32 = c::AF_INET6 as i32; + + // The following two structs use a union in the generated bindings but + // our cross-platform code expects a normal field so it's redefined here. + // As a consequence, we also need to redefine other structs that use this struct. + #[repr(C)] + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[repr(C)] + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[repr(C)] + pub struct ip_mreq { + pub imr_multiaddr: in_addr, + pub imr_interface: in_addr, + } + + #[repr(C)] + pub struct ipv6_mreq { + pub ipv6mr_multiaddr: in6_addr, + pub ipv6mr_interface: c_uint, + } + + #[repr(C)] + #[derive(Copy, Clone)] + pub struct sockaddr_in { + pub sin_family: ADDRESS_FAMILY, + pub sin_port: c_ushort, + pub sin_addr: in_addr, + pub sin_zero: [c_char; 8], + } + + #[repr(C)] + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + pub sin6_family: ADDRESS_FAMILY, + pub sin6_port: c_ushort, + pub sin6_flowinfo: c_ulong, + pub sin6_addr: in6_addr, + pub sin6_scope_id: c_ulong, + } + + pub unsafe fn send(socket: SOCKET, buf: *const c_void, len: c_int, flags: c_int) -> c_int { + unsafe { c::send(socket, buf.cast::(), len, flags) } + } + pub unsafe fn sendto( + socket: SOCKET, + buf: *const c_void, + len: c_int, + flags: c_int, + addr: *const SOCKADDR, + addrlen: c_int, + ) -> c_int { + unsafe { c::sendto(socket, buf.cast::(), len, flags, addr, addrlen) } + } + pub unsafe fn getaddrinfo( + node: *const c_char, + service: *const c_char, + hints: *const ADDRINFOA, + res: *mut *mut ADDRINFOA, + ) -> c_int { + unsafe { c::getaddrinfo(node.cast::(), service.cast::(), hints, res) } + } } pub struct Socket(OwnedSocket); @@ -102,8 +185,8 @@ where impl Socket { pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { let family = match *addr { - SocketAddr::V4(..) => c::AF_INET, - SocketAddr::V6(..) => c::AF_INET6, + SocketAddr::V4(..) => netc::AF_INET, + SocketAddr::V6(..) => netc::AF_INET6, }; let socket = unsafe { c::WSASocketW( @@ -157,7 +240,7 @@ impl Socket { return Err(io::Error::ZERO_TIMEOUT); } - let mut timeout = c::timeval { + let mut timeout = c::TIMEVAL { tv_sec: cmp::min(timeout.as_secs(), c_long::MAX as u64) as c_long, tv_usec: timeout.subsec_micros() as c_long, }; @@ -167,7 +250,7 @@ impl Socket { } let fds = { - let mut fds = unsafe { mem::zeroed::() }; + let mut fds = unsafe { mem::zeroed::() }; fds.fd_count = 1; fds.fd_array[0] = self.as_raw(); fds @@ -250,7 +333,7 @@ impl Socket { pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { // On unix when a socket is shut down all further reads return 0, so we // do the same on windows to map a shut down socket to returning EOF. - let length = cmp::min(bufs.len(), c::DWORD::MAX as usize) as c::DWORD; + let length = cmp::min(bufs.len(), u32::MAX as usize) as u32; let mut nread = 0; let mut flags = 0; let result = unsafe { @@ -295,8 +378,8 @@ impl Socket { buf: &mut [u8], flags: c_int, ) -> io::Result<(usize, SocketAddr)> { - let mut storage = unsafe { mem::zeroed::() }; - let mut addrlen = mem::size_of_val(&storage) as c::socklen_t; + let mut storage = unsafe { mem::zeroed::() }; + let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; let length = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; // On unix when a socket is shut down all further reads return 0, so we @@ -335,7 +418,7 @@ impl Socket { } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let length = cmp::min(bufs.len(), c::DWORD::MAX as usize) as c::DWORD; + let length = cmp::min(bufs.len(), u32::MAX as usize) as u32; let mut nwritten = 0; let result = unsafe { c::WSASend( @@ -371,7 +454,7 @@ impl Socket { } pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: c::DWORD = net::getsockopt(self, c::SOL_SOCKET, kind)?; + let raw: u32 = net::getsockopt(self, c::SOL_SOCKET, kind)?; if raw == 0 { Ok(None) } else { @@ -399,7 +482,7 @@ impl Socket { } pub fn set_linger(&self, linger: Option) -> io::Result<()> { - let linger = c::linger { + let linger = c::LINGER { l_onoff: linger.is_some() as c_ushort, l_linger: linger.unwrap_or_default().as_secs() as c_ushort, }; @@ -408,7 +491,7 @@ impl Socket { } pub fn linger(&self) -> io::Result> { - let val: c::linger = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; + let val: c::LINGER = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } @@ -436,7 +519,7 @@ impl Socket { pub unsafe fn from_raw(raw: c::SOCKET) -> Self { debug_assert_eq!(mem::size_of::(), mem::size_of::()); debug_assert_eq!(mem::align_of::(), mem::align_of::()); - Self::from_raw_socket(raw as RawSocket) + unsafe { Self::from_raw_socket(raw as RawSocket) } } } @@ -486,6 +569,6 @@ impl IntoRawSocket for Socket { impl FromRawSocket for Socket { unsafe fn from_raw_socket(raw_socket: RawSocket) -> Self { - Self(FromRawSocket::from_raw_socket(raw_socket)) + unsafe { Self(FromRawSocket::from_raw_socket(raw_socket)) } } } diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index 64d8b72aed282..5242bc9da31fe 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -5,19 +5,15 @@ #[cfg(test)] mod tests; -use crate::os::windows::prelude::*; - +use super::api::{self, WinError}; +use super::to_u16s; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::io; use crate::os::windows::ffi::EncodeWide; +use crate::os::windows::prelude::*; use crate::path::{self, PathBuf}; -use crate::ptr; -use crate::slice; use crate::sys::{c, cvt}; - -use super::{api, to_u16s}; +use crate::{fmt, io, ptr, slice}; pub fn errno() -> i32 { api::get_last_error().code as i32 @@ -51,10 +47,10 @@ pub fn error_string(mut errnum: i32) -> String { let res = c::FormatMessageW( flags | c::FORMAT_MESSAGE_FROM_SYSTEM | c::FORMAT_MESSAGE_IGNORE_INSERTS, module, - errnum as c::DWORD, + errnum as u32, 0, buf.as_mut_ptr(), - buf.len() as c::DWORD, + buf.len() as u32, ptr::null(), ) as usize; if res == 0 { @@ -80,7 +76,7 @@ pub fn error_string(mut errnum: i32) -> String { } pub struct Env { - base: c::LPWCH, + base: *mut c::WCHAR, iter: EnvIterator, } @@ -125,7 +121,7 @@ impl Iterator for Env { } #[derive(Clone)] -struct EnvIterator(c::LPWCH); +struct EnvIterator(*mut c::WCHAR); impl Iterator for EnvIterator { type Item = (OsString, OsString); @@ -302,16 +298,22 @@ pub fn getenv(k: &OsStr) -> Option { .ok() } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let k = to_u16s(k)?; - let v = to_u16s(v)?; +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + // SAFETY: We ensure that k and v are null-terminated wide strings. + unsafe { + let k = to_u16s(k)?; + let v = to_u16s(v)?; - cvt(unsafe { c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) }).map(drop) + cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop) + } } -pub fn unsetenv(n: &OsStr) -> io::Result<()> { - let v = to_u16s(n)?; - cvt(unsafe { c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) }).map(drop) +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { + // SAFETY: We ensure that v is a null-terminated wide strings. + unsafe { + let v = to_u16s(n)?; + cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop) + } } pub fn temp_dir() -> PathBuf { @@ -333,7 +335,7 @@ fn home_dir_crt() -> Option { buf, &mut sz, ) { - 0 if api::get_last_error().code != c::ERROR_INSUFFICIENT_BUFFER => 0, + 0 if api::get_last_error() != WinError::INSUFFICIENT_BUFFER => 0, 0 => sz, _ => sz - 1, // sz includes the null terminator } @@ -358,7 +360,7 @@ fn home_dir_crt() -> Option { super::fill_utf16_buf( |buf, mut sz| { match c::GetUserProfileDirectoryW(token, buf, &mut sz) { - 0 if api::get_last_error().code != c::ERROR_INSUFFICIENT_BUFFER => 0, + 0 if api::get_last_error() != WinError::INSUFFICIENT_BUFFER => 0, 0 => sz, _ => sz - 1, // sz includes the null terminator } @@ -382,7 +384,7 @@ pub fn home_dir() -> Option { } pub fn exit(code: i32) -> ! { - unsafe { c::ExitProcess(code as c::UINT) } + unsafe { c::ExitProcess(code as u32) } } pub fn getpid() -> u32 { diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index dfa938d4d5769..7d1b5aca1d5fe 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -1,18 +1,15 @@ -use crate::os::windows::prelude::*; - use crate::ffi::OsStr; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; -use crate::mem; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::os::windows::prelude::*; use crate::path::Path; -use crate::ptr; -use crate::slice; use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::Relaxed; -use crate::sys::c; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; -use crate::sys::hashmap_random_keys; +use crate::sys::pal::windows::api::{self, WinError}; +use crate::sys::{c, hashmap_random_keys}; use crate::sys_common::{FromInner, IntoInner}; +use crate::{mem, ptr}; //////////////////////////////////////////////////////////////////////////////// // Anonymous pipes @@ -124,20 +121,19 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res // testing strategy // For more info, see https://github.com/rust-lang/rust/pull/37677. if handle == c::INVALID_HANDLE_VALUE { - let err = io::Error::last_os_error(); - let raw_os_err = err.raw_os_error(); + let error = api::get_last_error(); if tries < 10 { - if raw_os_err == Some(c::ERROR_ACCESS_DENIED as i32) { + if error == WinError::ACCESS_DENIED { continue; } else if reject_remote_clients_flag != 0 - && raw_os_err == Some(c::ERROR_INVALID_PARAMETER as i32) + && error == WinError::INVALID_PARAMETER { reject_remote_clients_flag = 0; tries -= 1; continue; } } - return Err(err); + return Err(io::Error::from_raw_os_error(error.code as i32)); } ours = Handle::from_raw_handle(handle); break; @@ -156,7 +152,7 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res opts.share_mode(0); let size = mem::size_of::(); let mut sa = c::SECURITY_ATTRIBUTES { - nLength: size as c::DWORD, + nLength: size as u32, lpSecurityDescriptor: ptr::null_mut(), bInheritHandle: their_handle_inheritable as i32, }; @@ -182,7 +178,7 @@ pub fn spawn_pipe_relay( their_handle_inheritable: bool, ) -> io::Result { // We need this handle to live for the lifetime of the thread spawned below. - let source = source.duplicate()?; + let source = source.try_clone()?; // create a new pair of anon pipes. let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?; @@ -222,15 +218,6 @@ fn random_number() -> usize { } } -// Abstracts over `ReadFileEx` and `WriteFileEx` -type AlertableIoFn = unsafe extern "system" fn( - BorrowedHandle<'_>, - c::LPVOID, - c::DWORD, - c::LPOVERLAPPED, - c::LPOVERLAPPED_COMPLETION_ROUTINE, -) -> c::BOOL; - impl AnonPipe { pub fn handle(&self) -> &Handle { &self.inner @@ -238,14 +225,18 @@ impl AnonPipe { pub fn into_handle(self) -> Handle { self.inner } - fn duplicate(&self) -> io::Result { + + pub fn try_clone(&self) -> io::Result { self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner }) } pub fn read(&self, buf: &mut [u8]) -> io::Result { let result = unsafe { - let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD; - self.alertable_io_internal(c::ReadFileEx, buf.as_mut_ptr() as _, len) + let len = crate::cmp::min(buf.len(), u32::MAX as usize) as u32; + let ptr = buf.as_mut_ptr(); + self.alertable_io_internal(|overlapped, callback| { + c::ReadFileEx(self.inner.as_raw_handle(), ptr, len, overlapped, callback) + }) }; match result { @@ -260,8 +251,11 @@ impl AnonPipe { pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { let result = unsafe { - let len = crate::cmp::min(buf.capacity(), c::DWORD::MAX as usize) as c::DWORD; - self.alertable_io_internal(c::ReadFileEx, buf.as_mut().as_mut_ptr() as _, len) + let len = crate::cmp::min(buf.capacity(), u32::MAX as usize) as u32; + let ptr = buf.as_mut().as_mut_ptr().cast::(); + self.alertable_io_internal(|overlapped, callback| { + c::ReadFileEx(self.inner.as_raw_handle(), ptr, len, overlapped, callback) + }) }; match result { @@ -295,8 +289,10 @@ impl AnonPipe { pub fn write(&self, buf: &[u8]) -> io::Result { unsafe { - let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD; - self.alertable_io_internal(c::WriteFileEx, buf.as_ptr() as _, len) + let len = crate::cmp::min(buf.len(), u32::MAX as usize) as u32; + self.alertable_io_internal(|overlapped, callback| { + c::WriteFileEx(self.inner.as_raw_handle(), buf.as_ptr(), len, overlapped, callback) + }) } } @@ -326,9 +322,7 @@ impl AnonPipe { /// [Asynchronous Procedure Call]: https://docs.microsoft.com/en-us/windows/win32/sync/asynchronous-procedure-calls unsafe fn alertable_io_internal( &self, - io: AlertableIoFn, - buf: c::LPVOID, - len: c::DWORD, + io: impl FnOnce(&mut c::OVERLAPPED, c::LPOVERLAPPED_COMPLETION_ROUTINE) -> c::BOOL, ) -> io::Result { // Use "alertable I/O" to synchronize the pipe I/O. // This has four steps. @@ -366,20 +360,25 @@ impl AnonPipe { lpOverlapped: *mut c::OVERLAPPED, ) { // Set `async_result` using a pointer smuggled through `hEvent`. - let result = - AsyncResult { error: dwErrorCode, transferred: dwNumberOfBytesTransferred }; - *(*lpOverlapped).hEvent.cast::>() = Some(result); + // SAFETY: + // At this point, the OVERLAPPED struct will have been written to by the OS, + // except for our `hEvent` field which we set to a valid AsyncResult pointer (see below) + unsafe { + let result = + AsyncResult { error: dwErrorCode, transferred: dwNumberOfBytesTransferred }; + *(*lpOverlapped).hEvent.cast::>() = Some(result); + } } // STEP 1: Start the I/O operation. - let mut overlapped: c::OVERLAPPED = crate::mem::zeroed(); + let mut overlapped: c::OVERLAPPED = unsafe { crate::mem::zeroed() }; // `hEvent` is unused by `ReadFileEx` and `WriteFileEx`. // Therefore the documentation suggests using it to smuggle a pointer to the callback. overlapped.hEvent = core::ptr::addr_of_mut!(async_result) as *mut _; // Asynchronous read of the pipe. // If successful, `callback` will be called once it completes. - let result = io(self.inner.as_handle(), buf, len, &mut overlapped, Some(callback)); + let result = io(&mut overlapped, Some(callback)); if result == c::FALSE { // We can return here because the call failed. // After this we must not return until the I/O completes. @@ -390,7 +389,7 @@ impl AnonPipe { let result = loop { // STEP 2: Enter an alertable state. // The second parameter of `SleepEx` is used to make this sleep alertable. - c::SleepEx(c::INFINITE, c::TRUE); + unsafe { c::SleepEx(c::INFINITE, c::TRUE) }; if let Some(result) = async_result { break result; } @@ -478,8 +477,11 @@ impl<'a> AsyncPipe<'a> { fn schedule_read(&mut self) -> io::Result { assert_eq!(self.state, State::NotReading); let amt = unsafe { - let slice = slice_to_end(self.dst); - self.pipe.read_overlapped(slice, &mut *self.overlapped)? + if self.dst.capacity() == self.dst.len() { + let additional = if self.dst.capacity() == 0 { 16 } else { 1 }; + self.dst.reserve(additional); + } + self.pipe.read_overlapped(self.dst.spare_capacity_mut(), &mut *self.overlapped)? }; // If this read finished immediately then our overlapped event will @@ -559,13 +561,3 @@ impl<'a> Drop for AsyncPipe<'a> { } } } - -unsafe fn slice_to_end(v: &mut Vec) -> &mut [u8] { - if v.capacity() == 0 { - v.reserve(16); - } - if v.capacity() == v.len() { - v.reserve(1); - } - slice::from_raw_parts_mut(v.as_mut_ptr().add(v.len()), v.capacity() - v.len()) -} diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs index e4ab2ca7da1ce..06eae5a07b068 100644 --- a/library/std/src/sys/pal/windows/process.rs +++ b/library/std/src/sys/pal/windows/process.rs @@ -3,33 +3,28 @@ #[cfg(test)] mod tests; -use crate::cmp; +use core::ffi::c_void; + +use super::api::{self, WinError}; use crate::collections::BTreeMap; -use crate::env; use crate::env::consts::{EXE_EXTENSION, EXE_SUFFIX}; use crate::ffi::{OsStr, OsString}; -use crate::fmt; use crate::io::{self, Error, ErrorKind}; -use crate::mem; use crate::mem::MaybeUninit; use crate::num::NonZero; use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle}; use crate::path::{Path, PathBuf}; -use crate::ptr; use crate::sync::Mutex; use crate::sys::args::{self, Arg}; -use crate::sys::c::{self, NonZeroDWORD, EXIT_FAILURE, EXIT_SUCCESS}; -use crate::sys::cvt; +use crate::sys::c::{self, EXIT_FAILURE, EXIT_SUCCESS}; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; -use crate::sys::path; use crate::sys::pipe::{self, AnonPipe}; -use crate::sys::stdio; +use crate::sys::{cvt, path, stdio}; use crate::sys_common::process::{CommandEnv, CommandEnvs}; use crate::sys_common::IntoInner; - -use core::ffi::c_void; +use crate::{cmp, env, fmt, mem, ptr}; //////////////////////////////////////////////////////////////////////////////// // Command @@ -161,6 +156,7 @@ pub struct Command { env: CommandEnv, cwd: Option, flags: u32, + show_window: Option, detach: bool, // not currently exposed in std::process stdin: Option, stdout: Option, @@ -171,7 +167,7 @@ pub struct Command { pub enum Stdio { Inherit, - InheritSpecific { from_stdio_id: c::DWORD }, + InheritSpecific { from_stdio_id: u32 }, Null, MakePipe, Pipe(AnonPipe), @@ -192,6 +188,7 @@ impl Command { env: Default::default(), cwd: None, flags: 0, + show_window: None, detach: false, stdin: None, stdout: None, @@ -222,6 +219,9 @@ impl Command { pub fn creation_flags(&mut self, flags: u32) { self.flags = flags; } + pub fn show_window(&mut self, cmd_show: Option) { + self.show_window = cmd_show; + } pub fn force_quotes(&mut self, enabled: bool) { self.force_quotes_enabled = enabled; @@ -335,6 +335,11 @@ impl Command { si.hStdError = stderr.as_raw_handle(); } + if let Some(cmd_show) = self.show_window { + si.dwFlags |= c::STARTF_USESHOWWINDOW; + si.wShowWindow = cmd_show; + } + let si_ptr: *mut c::STARTUPINFOW; let mut proc_thread_attribute_list; @@ -352,7 +357,7 @@ impl Command { }; si_ptr = core::ptr::addr_of_mut!(si_ex) as _; } else { - si.cb = mem::size_of::() as c::DWORD; + si.cb = mem::size_of::() as u32; si_ptr = core::ptr::addr_of_mut!(si) as _; } @@ -537,7 +542,7 @@ where None } -/// Check if a file exists without following symlinks. +/// Checks if a file exists without following symlinks. fn program_exists(path: &Path) -> Option> { unsafe { let path = args::to_user_path(path).ok()?; @@ -554,12 +559,12 @@ fn program_exists(path: &Path) -> Option> { } impl Stdio { - fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option) -> io::Result { + fn to_handle(&self, stdio_id: u32, pipe: &mut Option) -> io::Result { let use_stdio_id = |stdio_id| match stdio::get_handle(stdio_id) { Ok(io) => unsafe { let io = Handle::from_raw_handle(io); let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); - io.into_raw_handle(); + let _ = io.into_raw_handle(); // Don't close the handle ret }, // If no stdio handle is available, then propagate the null value. @@ -589,7 +594,7 @@ impl Stdio { Stdio::Null => { let size = mem::size_of::(); let mut sa = c::SECURITY_ATTRIBUTES { - nLength: size as c::DWORD, + nLength: size as u32, lpSecurityDescriptor: ptr::null_mut(), bInheritHandle: 1, }; @@ -645,12 +650,12 @@ impl Process { pub fn kill(&mut self) -> io::Result<()> { let result = unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) }; if result == c::FALSE { - let error = unsafe { c::GetLastError() }; + let error = api::get_last_error(); // TerminateProcess returns ERROR_ACCESS_DENIED if the process has already been // terminated (by us, or for any other reason). So check if the process was actually // terminated, and if so, do not return an error. - if error != c::ERROR_ACCESS_DENIED || self.try_wait().is_err() { - return Err(crate::io::Error::from_raw_os_error(error as i32)); + if error != WinError::ACCESS_DENIED || self.try_wait().is_err() { + return Err(crate::io::Error::from_raw_os_error(error.code as i32)); } } Ok(()) @@ -701,11 +706,11 @@ impl Process { } #[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] -pub struct ExitStatus(c::DWORD); +pub struct ExitStatus(u32); impl ExitStatus { pub fn exit_ok(&self) -> Result<(), ExitStatusError> { - match NonZeroDWORD::try_from(self.0) { + match NonZero::::try_from(self.0) { /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), /* was zero, couldn't convert */ Err(_) => Ok(()), } @@ -715,9 +720,9 @@ impl ExitStatus { } } -/// Converts a raw `c::DWORD` to a type-safe `ExitStatus` by wrapping it without copying. -impl From for ExitStatus { - fn from(u: c::DWORD) -> ExitStatus { +/// Converts a raw `u32` to a type-safe `ExitStatus` by wrapping it without copying. +impl From for ExitStatus { + fn from(u: u32) -> ExitStatus { ExitStatus(u) } } @@ -738,7 +743,7 @@ impl fmt::Display for ExitStatus { } #[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitStatusError(c::NonZeroDWORD); +pub struct ExitStatusError(NonZero); impl Into for ExitStatusError { fn into(self) -> ExitStatus { @@ -753,7 +758,7 @@ impl ExitStatusError { } #[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(c::DWORD); +pub struct ExitCode(u32); impl ExitCode { pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); @@ -767,13 +772,13 @@ impl ExitCode { impl From for ExitCode { fn from(code: u8) -> Self { - ExitCode(c::DWORD::from(code)) + ExitCode(u32::from(code)) } } impl From for ExitCode { fn from(code: u32) -> Self { - ExitCode(c::DWORD::from(code)) + ExitCode(u32::from(code)) } } diff --git a/library/std/src/sys/pal/windows/process/tests.rs b/library/std/src/sys/pal/windows/process/tests.rs index 3fc0c75240c33..65325fa64a077 100644 --- a/library/std/src/sys/pal/windows/process/tests.rs +++ b/library/std/src/sys/pal/windows/process/tests.rs @@ -1,5 +1,4 @@ -use super::make_command_line; -use super::Arg; +use super::{make_command_line, Arg}; use crate::env; use crate::ffi::{OsStr, OsString}; use crate::process::Command; diff --git a/library/std/src/sys/pal/windows/rand.rs b/library/std/src/sys/pal/windows/rand.rs index e427546222aea..e366bb995626a 100644 --- a/library/std/src/sys/pal/windows/rand.rs +++ b/library/std/src/sys/pal/windows/rand.rs @@ -1,6 +1,6 @@ +use core::{mem, ptr}; + use crate::sys::c; -use core::mem; -use core::ptr; #[cfg(not(target_vendor = "win7"))] #[inline] @@ -20,7 +20,7 @@ pub fn hashmap_random_keys() -> (u64, u64) { let mut v = (0, 0); let ret = unsafe { - c::RtlGenRandom(ptr::addr_of_mut!(v).cast::(), mem::size_of_val(&v) as c::ULONG) + c::RtlGenRandom(ptr::addr_of_mut!(v).cast::(), mem::size_of_val(&v) as u32) }; if ret != 0 { v } else { panic!("RNG broken: {}", io::Error::last_os_error()) } diff --git a/library/std/src/sys/pal/windows/stack_overflow.rs b/library/std/src/sys/pal/windows/stack_overflow.rs index f93f31026f818..467e21ab56a28 100644 --- a/library/std/src/sys/pal/windows/stack_overflow.rs +++ b/library/std/src/sys/pal/windows/stack_overflow.rs @@ -4,14 +4,15 @@ use crate::sys::c; use crate::thread; /// Reserve stack space for use in stack overflow exceptions. -pub unsafe fn reserve_stack() { - let result = c::SetThreadStackGuarantee(&mut 0x5000); +pub fn reserve_stack() { + let result = unsafe { c::SetThreadStackGuarantee(&mut 0x5000) }; // Reserving stack space is not critical so we allow it to fail in the released build of libstd. // We still use debug assert here so that CI will test that we haven't made a mistake calling the function. debug_assert_ne!(result, 0, "failed to reserve stack space for exception handling"); } -unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> c::LONG { +unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POINTERS) -> i32 { + // SAFETY: It's up to the caller (which in this case is the OS) to ensure that `ExceptionInfo` is valid. unsafe { let rec = &(*(*ExceptionInfo).ExceptionRecord); let code = rec.ExceptionCode; @@ -26,11 +27,14 @@ unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POIN } } -pub unsafe fn init() { - let result = c::AddVectoredExceptionHandler(0, Some(vectored_handler)); - // Similar to the above, adding the stack overflow handler is allowed to fail - // but a debug assert is used so CI will still test that it normally works. - debug_assert!(!result.is_null(), "failed to install exception handler"); +pub fn init() { + // SAFETY: `vectored_handler` has the correct ABI and is safe to call during exception handling. + unsafe { + let result = c::AddVectoredExceptionHandler(0, Some(vectored_handler)); + // Similar to the above, adding the stack overflow handler is allowed to fail + // but a debug assert is used so CI will still test that it normally works. + debug_assert!(!result.is_null(), "failed to install exception handler"); + } // Set the thread stack guarantee for the main thread. reserve_stack(); } diff --git a/library/std/src/sys/pal/windows/stdio.rs b/library/std/src/sys/pal/windows/stdio.rs index 96c23f82aec2e..575f2250eb91c 100644 --- a/library/std/src/sys/pal/windows/stdio.rs +++ b/library/std/src/sys/pal/windows/stdio.rs @@ -1,16 +1,13 @@ #![unstable(issue = "none", feature = "windows_stdio")] -use super::api; -use crate::cmp; -use crate::io; +use core::str::utf8_char_width; + +use super::api::{self, WinError}; use crate::mem::MaybeUninit; use crate::os::windows::io::{FromRawHandle, IntoRawHandle}; -use crate::ptr; -use crate::str; -use crate::sys::c; -use crate::sys::cvt; use crate::sys::handle::Handle; -use core::str::utf8_char_width; +use crate::sys::{c, cvt}; +use crate::{cmp, io, ptr, str}; #[cfg(test)] mod tests; @@ -68,7 +65,7 @@ const MAX_BUFFER_SIZE: usize = 8192; // UTF-16 to UTF-8. pub const STDIN_BUF_SIZE: usize = MAX_BUFFER_SIZE / 2 * 3; -pub fn get_handle(handle_id: c::DWORD) -> io::Result { +pub fn get_handle(handle_id: u32) -> io::Result { let handle = unsafe { c::GetStdHandle(handle_id) }; if handle == c::INVALID_HANDLE_VALUE { Err(io::Error::last_os_error()) @@ -87,11 +84,7 @@ fn is_console(handle: c::HANDLE) -> bool { unsafe { c::GetConsoleMode(handle, &mut mode) != 0 } } -fn write( - handle_id: c::DWORD, - data: &[u8], - incomplete_utf8: &mut IncompleteUtf8, -) -> io::Result { +fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> io::Result { if data.is_empty() { return Ok(0); } @@ -101,7 +94,7 @@ fn write( unsafe { let handle = Handle::from_raw_handle(handle); let ret = handle.write(data); - handle.into_raw_handle(); // Don't close the handle + let _ = handle.into_raw_handle(); // Don't close the handle return ret; } } @@ -182,12 +175,12 @@ fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result io::Result { debug_assert!(data.len() < u32::MAX as usize); let mut written = 0; cvt(unsafe { - c::WriteConsoleW( - handle, - data.as_ptr() as c::LPCVOID, - data.len() as u32, - &mut written, - ptr::null_mut(), - ) + c::WriteConsoleW(handle, data.as_ptr(), data.len() as u32, &mut written, ptr::null_mut()) })?; Ok(written as usize) } @@ -256,7 +243,7 @@ impl io::Read for Stdin { unsafe { let handle = Handle::from_raw_handle(handle); let ret = handle.read(buf); - handle.into_raw_handle(); // Don't close the handle + let _ = handle.into_raw_handle(); // Don't close the handle return ret; } } @@ -347,9 +334,9 @@ fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit]) -> io::Result() as c::ULONG, + nLength: crate::mem::size_of::() as u32, nInitialChars: 0, dwCtrlWakeupMask: CTRL_Z_MASK, dwControlKeyState: 0, @@ -361,7 +348,7 @@ fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit]) -> io::Result]) -> io::Result]) -> io::Result io::Result { - debug_assert!(utf16.len() <= c::c_int::MAX as usize); - debug_assert!(utf8.len() <= c::c_int::MAX as usize); + debug_assert!(utf16.len() <= i32::MAX as usize); + debug_assert!(utf8.len() <= i32::MAX as usize); if utf16.is_empty() { return Ok(0); @@ -396,9 +383,9 @@ fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result { c::CP_UTF8, // CodePage c::WC_ERR_INVALID_CHARS, // dwFlags utf16.as_ptr(), // lpWideCharStr - utf16.len() as c::c_int, // cchWideChar + utf16.len() as i32, // cchWideChar utf8.as_mut_ptr(), // lpMultiByteStr - utf8.len() as c::c_int, // cbMultiByte + utf8.len() as i32, // cbMultiByte ptr::null(), // lpDefaultChar ptr::null_mut(), // lpUsedDefaultChar ) diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index 70099e0a3b560..28bce529cd991 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -1,18 +1,15 @@ +use core::ffi::c_void; + +use super::time::WaitableTimer; +use super::to_u16s; use crate::ffi::CStr; -use crate::io; use crate::num::NonZero; -use crate::os::windows::io::AsRawHandle; -use crate::os::windows::io::HandleOrNull; -use crate::ptr; -use crate::sys::c; +use crate::os::windows::io::{AsRawHandle, HandleOrNull}; use crate::sys::handle::Handle; -use crate::sys::stack_overflow; +use crate::sys::{c, stack_overflow}; use crate::sys_common::FromInner; use crate::time::Duration; -use core::ffi::c_void; - -use super::time::WaitableTimer; -use super::to_u16s; +use crate::{io, ptr}; pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; @@ -27,29 +24,35 @@ impl Thread { // CreateThread rounds up values for the stack size to the nearest page size (at least 4kb). // If a value of zero is given then the default stack size is used instead. - let ret = c::CreateThread( - ptr::null_mut(), - stack, - Some(thread_start), - p as *mut _, - c::STACK_SIZE_PARAM_IS_A_RESERVATION, - ptr::null_mut(), - ); - let ret = HandleOrNull::from_raw_handle(ret); + // SAFETY: `thread_start` has the right ABI for a thread's entry point. + // `p` is simply passed through to the new thread without being touched. + let ret = unsafe { + let ret = c::CreateThread( + ptr::null_mut(), + stack, + Some(thread_start), + p as *mut _, + c::STACK_SIZE_PARAM_IS_A_RESERVATION, + ptr::null_mut(), + ); + HandleOrNull::from_raw_handle(ret) + }; return if let Ok(handle) = ret.try_into() { Ok(Thread { handle: Handle::from_inner(handle) }) } else { // The thread failed to start and as a result p was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. - drop(Box::from_raw(p)); + unsafe { drop(Box::from_raw(p)) }; Err(io::Error::last_os_error()) }; - unsafe extern "system" fn thread_start(main: *mut c_void) -> c::DWORD { + unsafe extern "system" fn thread_start(main: *mut c_void) -> u32 { // Next, reserve some stack space for if we otherwise run out of stack. stack_overflow::reserve_stack(); // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); + // SAFETY: We are simply recreating the box that was leaked earlier. + // It's the responsibility of the one who call `Thread::new` to ensure this is safe to call here. + unsafe { Box::from_raw(main as *mut Box)() }; 0 } } @@ -69,7 +72,7 @@ impl Thread { /// /// `name` must end with a zero value pub unsafe fn set_name_wide(name: &[u16]) { - c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()); + unsafe { c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()) }; } pub fn join(self) { diff --git a/library/std/src/sys/pal/windows/thread_local_dtor.rs b/library/std/src/sys/pal/windows/thread_local_dtor.rs deleted file mode 100644 index cf542d2bfb838..0000000000000 --- a/library/std/src/sys/pal/windows/thread_local_dtor.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! Implements thread-local destructors that are not associated with any -//! particular data. - -#![unstable(feature = "thread_local_internals", issue = "none")] -#![cfg(target_thread_local)] - -pub use super::thread_local_key::register_keyless_dtor as register_dtor; diff --git a/library/std/src/sys/pal/windows/thread_local_key.rs b/library/std/src/sys/pal/windows/thread_local_key.rs deleted file mode 100644 index e5ba619fc6ba4..0000000000000 --- a/library/std/src/sys/pal/windows/thread_local_key.rs +++ /dev/null @@ -1,351 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::ptr; -use crate::sync::atomic::{ - AtomicPtr, AtomicU32, - Ordering::{AcqRel, Acquire, Relaxed, Release}, -}; -use crate::sys::c; - -#[cfg(test)] -mod tests; - -// Using a per-thread list avoids the problems in synchronizing global state. -#[thread_local] -#[cfg(target_thread_local)] -static DESTRUCTORS: crate::cell::RefCell> = - crate::cell::RefCell::new(Vec::new()); - -// Ensure this can never be inlined because otherwise this may break in dylibs. -// See #44391. -#[inline(never)] -#[cfg(target_thread_local)] -pub unsafe fn register_keyless_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - dtors_used(); - match DESTRUCTORS.try_borrow_mut() { - Ok(mut dtors) => dtors.push((t, dtor)), - Err(_) => rtabort!("global allocator may not use TLS"), - } -} - -#[inline(never)] // See comment above -#[cfg(target_thread_local)] -/// Runs destructors. This should not be called until thread exit. -unsafe fn run_keyless_dtors() { - // Drop all the destructors. - // - // Note: While this is potentially an infinite loop, it *should* be - // the case that this loop always terminates because we provide the - // guarantee that a TLS key cannot be set after it is flagged for - // destruction. - loop { - // Use a let-else binding to ensure the `RefCell` guard is dropped - // immediately. Otherwise, a panic would occur if a TLS destructor - // tries to access the list. - let Some((ptr, dtor)) = DESTRUCTORS.borrow_mut().pop() else { - break; - }; - (dtor)(ptr); - } - // We're done so free the memory. - DESTRUCTORS.replace(Vec::new()); -} - -type Key = c::DWORD; -type Dtor = unsafe extern "C" fn(*mut u8); - -// Turns out, like pretty much everything, Windows is pretty close the -// functionality that Unix provides, but slightly different! In the case of -// TLS, Windows does not provide an API to provide a destructor for a TLS -// variable. This ends up being pretty crucial to this implementation, so we -// need a way around this. -// -// The solution here ended up being a little obscure, but fear not, the -// internet has informed me [1][2] that this solution is not unique (no way -// I could have thought of it as well!). The key idea is to insert some hook -// somewhere to run arbitrary code on thread termination. With this in place -// we'll be able to run anything we like, including all TLS destructors! -// -// To accomplish this feat, we perform a number of threads, all contained -// within this module: -// -// * All TLS destructors are tracked by *us*, not the Windows runtime. This -// means that we have a global list of destructors for each TLS key that -// we know about. -// * When a thread exits, we run over the entire list and run dtors for all -// non-null keys. This attempts to match Unix semantics in this regard. -// -// For more details and nitty-gritty, see the code sections below! -// -// [1]: https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way -// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base/threading/thread_local_storage_win.cc#L42 - -pub struct StaticKey { - /// The key value shifted up by one. Since TLS_OUT_OF_INDEXES == DWORD::MAX - /// is not a valid key value, this allows us to use zero as sentinel value - /// without risking overflow. - key: AtomicU32, - dtor: Option, - next: AtomicPtr, - /// Currently, destructors cannot be unregistered, so we cannot use racy - /// initialization for keys. Instead, we need synchronize initialization. - /// Use the Windows-provided `Once` since it does not require TLS. - once: UnsafeCell, -} - -impl StaticKey { - #[inline] - pub const fn new(dtor: Option) -> StaticKey { - StaticKey { - key: AtomicU32::new(0), - dtor, - next: AtomicPtr::new(ptr::null_mut()), - once: UnsafeCell::new(c::INIT_ONCE_STATIC_INIT), - } - } - - #[inline] - pub unsafe fn set(&'static self, val: *mut u8) { - let r = c::TlsSetValue(self.key(), val.cast()); - debug_assert_eq!(r, c::TRUE); - } - - #[inline] - pub unsafe fn get(&'static self) -> *mut u8 { - c::TlsGetValue(self.key()).cast() - } - - #[inline] - unsafe fn key(&'static self) -> Key { - match self.key.load(Acquire) { - 0 => self.init(), - key => key - 1, - } - } - - #[cold] - unsafe fn init(&'static self) -> Key { - if self.dtor.is_some() { - dtors_used(); - let mut pending = c::FALSE; - let r = c::InitOnceBeginInitialize(self.once.get(), 0, &mut pending, ptr::null_mut()); - assert_eq!(r, c::TRUE); - - if pending == c::FALSE { - // Some other thread initialized the key, load it. - self.key.load(Relaxed) - 1 - } else { - let key = c::TlsAlloc(); - if key == c::TLS_OUT_OF_INDEXES { - // Wakeup the waiting threads before panicking to avoid deadlock. - c::InitOnceComplete(self.once.get(), c::INIT_ONCE_INIT_FAILED, ptr::null_mut()); - panic!("out of TLS indexes"); - } - - register_dtor(self); - - // Release-storing the key needs to be the last thing we do. - // This is because in `fn key()`, other threads will do an acquire load of the key, - // and if that sees this write then it will entirely bypass the `InitOnce`. We thus - // need to establish synchronization through `key`. In particular that acquire load - // must happen-after the register_dtor above, to ensure the dtor actually runs! - self.key.store(key + 1, Release); - - let r = c::InitOnceComplete(self.once.get(), 0, ptr::null_mut()); - debug_assert_eq!(r, c::TRUE); - - key - } - } else { - // If there is no destructor to clean up, we can use racy initialization. - - let key = c::TlsAlloc(); - assert_ne!(key, c::TLS_OUT_OF_INDEXES, "out of TLS indexes"); - - match self.key.compare_exchange(0, key + 1, AcqRel, Acquire) { - Ok(_) => key, - Err(new) => { - // Some other thread completed initialization first, so destroy - // our key and use theirs. - let r = c::TlsFree(key); - debug_assert_eq!(r, c::TRUE); - new - 1 - } - } - } - } -} - -unsafe impl Send for StaticKey {} -unsafe impl Sync for StaticKey {} - -// ------------------------------------------------------------------------- -// Dtor registration -// -// Windows has no native support for running destructors so we manage our own -// list of destructors to keep track of how to destroy keys. We then install a -// callback later to get invoked whenever a thread exits, running all -// appropriate destructors. -// -// Currently unregistration from this list is not supported. A destructor can be -// registered but cannot be unregistered. There's various simplifying reasons -// for doing this, the big ones being: -// -// 1. Currently we don't even support deallocating TLS keys, so normal operation -// doesn't need to deallocate a destructor. -// 2. There is no point in time where we know we can unregister a destructor -// because it could always be getting run by some remote thread. -// -// Typically processes have a statically known set of TLS keys which is pretty -// small, and we'd want to keep this memory alive for the whole process anyway -// really. - -static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); - -/// Should only be called once per key, otherwise loops or breaks may occur in -/// the linked list. -unsafe fn register_dtor(key: &'static StaticKey) { - // Ensure this is never run when native thread locals are available. - assert_eq!(false, cfg!(target_thread_local)); - let this = <*const StaticKey>::cast_mut(key); - // Use acquire ordering to pass along the changes done by the previously - // registered keys when we store the new head with release ordering. - let mut head = DTORS.load(Acquire); - loop { - key.next.store(head, Relaxed); - match DTORS.compare_exchange_weak(head, this, Release, Acquire) { - Ok(_) => break, - Err(new) => head = new, - } - } -} - -// ------------------------------------------------------------------------- -// Where the Magic (TM) Happens -// -// If you're looking at this code, and wondering "what is this doing?", -// you're not alone! I'll try to break this down step by step: -// -// # What's up with CRT$XLB? -// -// For anything about TLS destructors to work on Windows, we have to be able -// to run *something* when a thread exits. To do so, we place a very special -// static in a very special location. If this is encoded in just the right -// way, the kernel's loader is apparently nice enough to run some function -// of ours whenever a thread exits! How nice of the kernel! -// -// Lots of detailed information can be found in source [1] above, but the -// gist of it is that this is leveraging a feature of Microsoft's PE format -// (executable format) which is not actually used by any compilers today. -// This apparently translates to any callbacks in the ".CRT$XLB" section -// being run on certain events. -// -// So after all that, we use the compiler's #[link_section] feature to place -// a callback pointer into the magic section so it ends up being called. -// -// # What's up with this callback? -// -// The callback specified receives a number of parameters from... someone! -// (the kernel? the runtime? I'm not quite sure!) There are a few events that -// this gets invoked for, but we're currently only interested on when a -// thread or a process "detaches" (exits). The process part happens for the -// last thread and the thread part happens for any normal thread. -// -// # Ok, what's up with running all these destructors? -// -// This will likely need to be improved over time, but this function -// attempts a "poor man's" destructor callback system. Once we've got a list -// of what to run, we iterate over all keys, check their values, and then run -// destructors if the values turn out to be non null (setting them to null just -// beforehand). We do this a few times in a loop to basically match Unix -// semantics. If we don't reach a fixed point after a short while then we just -// inevitably leak something most likely. -// -// # The article mentions weird stuff about "/INCLUDE"? -// -// It sure does! Specifically we're talking about this quote: -// -// The Microsoft run-time library facilitates this process by defining a -// memory image of the TLS Directory and giving it the special name -// “__tls_used” (Intel x86 platforms) or “_tls_used” (other platforms). The -// linker looks for this memory image and uses the data there to create the -// TLS Directory. Other compilers that support TLS and work with the -// Microsoft linker must use this same technique. -// -// Basically what this means is that if we want support for our TLS -// destructors/our hook being called then we need to make sure the linker does -// not omit this symbol. Otherwise it will omit it and our callback won't be -// wired up. -// -// We don't actually use the `/INCLUDE` linker flag here like the article -// mentions because the Rust compiler doesn't propagate linker flags, but -// instead we use a shim function which performs a volatile 1-byte load from -// the address of the symbol to ensure it sticks around. - -#[link_section = ".CRT$XLB"] -#[cfg_attr(miri, used)] // Miri only considers explicitly `#[used]` statics for `lookup_link_section` -pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::LPVOID) = - on_tls_callback; - -fn dtors_used() { - // we don't want LLVM eliminating p_thread_callback when destructors are used. - // when the symbol makes it to the linker the linker will take over - unsafe { crate::intrinsics::volatile_load(&p_thread_callback) }; -} - -unsafe extern "system" fn on_tls_callback(_h: c::LPVOID, dwReason: c::DWORD, _pv: c::LPVOID) { - if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH { - #[cfg(not(target_thread_local))] - run_dtors(); - #[cfg(target_thread_local)] - run_keyless_dtors(); - } - - // See comments above for what this is doing. Note that we don't need this - // trickery on GNU windows, just on MSVC. - #[cfg(all(target_env = "msvc", not(target_thread_local)))] - { - extern "C" { - static _tls_used: u8; - } - crate::intrinsics::volatile_load(&_tls_used); - } -} - -#[cfg(not(target_thread_local))] -unsafe fn run_dtors() { - for _ in 0..5 { - let mut any_run = false; - - // Use acquire ordering to observe key initialization. - let mut cur = DTORS.load(Acquire); - while !cur.is_null() { - let pre_key = (*cur).key.load(Acquire); - let dtor = (*cur).dtor.unwrap(); - cur = (*cur).next.load(Relaxed); - - // In StaticKey::init, we register the dtor before setting `key`. - // So if one thread's `run_dtors` races with another thread executing `init` on the same - // `StaticKey`, we can encounter a key of 0 here. That means this key was never - // initialized in this thread so we can safely skip it. - if pre_key == 0 { - continue; - } - // If this is non-zero, then via the `Acquire` load above we synchronized with - // everything relevant for this key. (It's not clear that this is needed, since the - // release-acquire pair on DTORS also establishes synchronization, but better safe than - // sorry.) - let key = pre_key - 1; - - let ptr = c::TlsGetValue(key); - if !ptr.is_null() { - c::TlsSetValue(key, ptr::null_mut()); - dtor(ptr as *mut _); - any_run = true; - } - } - - if !any_run { - break; - } - } -} diff --git a/library/std/src/sys/pal/windows/thread_local_key/tests.rs b/library/std/src/sys/pal/windows/thread_local_key/tests.rs deleted file mode 100644 index 4119f99096842..0000000000000 --- a/library/std/src/sys/pal/windows/thread_local_key/tests.rs +++ /dev/null @@ -1,57 +0,0 @@ -// This file only tests the thread local key fallback. -// Windows targets with native thread local support do not use this. -#![cfg(not(target_thread_local))] - -use super::StaticKey; -use crate::ptr; - -#[test] -fn smoke() { - static K1: StaticKey = StaticKey::new(None); - static K2: StaticKey = StaticKey::new(None); - - unsafe { - assert!(K1.get().is_null()); - assert!(K2.get().is_null()); - K1.set(ptr::without_provenance_mut(1)); - K2.set(ptr::without_provenance_mut(2)); - assert_eq!(K1.get() as usize, 1); - assert_eq!(K2.get() as usize, 2); - } -} - -#[test] -fn destructors() { - use crate::mem::ManuallyDrop; - use crate::sync::Arc; - use crate::thread; - - unsafe extern "C" fn destruct(ptr: *mut u8) { - drop(Arc::from_raw(ptr as *const ())); - } - - static KEY: StaticKey = StaticKey::new(Some(destruct)); - - let shared1 = Arc::new(()); - let shared2 = Arc::clone(&shared1); - - unsafe { - assert!(KEY.get().is_null()); - KEY.set(Arc::into_raw(shared1) as *mut u8); - } - - thread::spawn(move || unsafe { - assert!(KEY.get().is_null()); - KEY.set(Arc::into_raw(shared2) as *mut u8); - }) - .join() - .unwrap(); - - // Leak the Arc, let the TLS destructor clean it up. - let shared1 = unsafe { ManuallyDrop::new(Arc::from_raw(KEY.get() as *const ())) }; - assert_eq!( - Arc::strong_count(&shared1), - 1, - "destructor should have dropped the other reference on thread exit" - ); -} diff --git a/library/std/src/sys/pal/windows/time.rs b/library/std/src/sys/pal/windows/time.rs index 09e78a29304f9..d9010e3996109 100644 --- a/library/std/src/sys/pal/windows/time.rs +++ b/library/std/src/sys/pal/windows/time.rs @@ -1,13 +1,12 @@ +use core::hash::{Hash, Hasher}; +use core::ops::Neg; + use crate::cmp::Ordering; -use crate::fmt; -use crate::mem; use crate::ptr::null; use crate::sys::c; use crate::sys_common::IntoInner; use crate::time::Duration; - -use core::hash::{Hash, Hasher}; -use core::ops::Neg; +use crate::{fmt, mem}; const NANOS_PER_SEC: u64 = 1_000_000_000; const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100; @@ -76,8 +75,8 @@ impl SystemTime { fn from_intervals(intervals: i64) -> SystemTime { SystemTime { t: c::FILETIME { - dwLowDateTime: intervals as c::DWORD, - dwHighDateTime: (intervals >> 32) as c::DWORD, + dwLowDateTime: intervals as u32, + dwHighDateTime: (intervals >> 32) as u32, }, } } @@ -166,13 +165,12 @@ fn intervals2dur(intervals: u64) -> Duration { mod perf_counter { use super::NANOS_PER_SEC; use crate::sync::atomic::{AtomicU64, Ordering}; - use crate::sys::c; - use crate::sys::cvt; + use crate::sys::{c, cvt}; use crate::sys_common::mul_div_u64; use crate::time::Duration; pub struct PerformanceCounterInstant { - ts: c::LARGE_INTEGER, + ts: i64, } impl PerformanceCounterInstant { pub fn now() -> Self { @@ -196,7 +194,7 @@ mod perf_counter { } } - fn frequency() -> c::LARGE_INTEGER { + fn frequency() -> i64 { // Either the cached result of `QueryPerformanceFrequency` or `0` for // uninitialized. Storing this as a single `AtomicU64` allows us to use // `Relaxed` operations, as we are only interested in the effects on a @@ -206,7 +204,7 @@ mod perf_counter { let cached = FREQUENCY.load(Ordering::Relaxed); // If a previous thread has filled in this global state, use that. if cached != 0 { - return cached as c::LARGE_INTEGER; + return cached as i64; } // ... otherwise learn for ourselves ... let mut frequency = 0; @@ -218,8 +216,8 @@ mod perf_counter { frequency } - fn query() -> c::LARGE_INTEGER { - let mut qpc_value: c::LARGE_INTEGER = 0; + fn query() -> i64 { + let mut qpc_value: i64 = 0; cvt(unsafe { c::QueryPerformanceCounter(&mut qpc_value) }).unwrap(); qpc_value } @@ -230,7 +228,7 @@ pub(super) struct WaitableTimer { handle: c::HANDLE, } impl WaitableTimer { - /// Create a high-resolution timer. Will fail before Windows 10, version 1803. + /// Creates a high-resolution timer. Will fail before Windows 10, version 1803. pub fn high_resolution() -> Result { let handle = unsafe { c::CreateWaitableTimerExW( diff --git a/library/std/src/sys/pal/xous/alloc.rs b/library/std/src/sys/pal/xous/alloc.rs index 601411173aacb..9ea43445d0206 100644 --- a/library/std/src/sys/pal/xous/alloc.rs +++ b/library/std/src/sys/pal/xous/alloc.rs @@ -46,10 +46,8 @@ unsafe impl GlobalAlloc for System { } mod lock { - use crate::sync::atomic::{ - AtomicI32, - Ordering::{Acquire, Release}, - }; + use crate::sync::atomic::AtomicI32; + use crate::sync::atomic::Ordering::{Acquire, Release}; static LOCKED: AtomicI32 = AtomicI32::new(0); diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index 68189bcc2e377..961d45c5e834f 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -1,4 +1,4 @@ -#![deny(unsafe_op_in_unsafe_fn)] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod alloc; #[path = "../unsupported/args.rs"] @@ -17,7 +17,6 @@ pub mod pipe; pub mod process; pub mod stdio; pub mod thread; -pub mod thread_local_key; pub mod time; #[path = "../unsupported/common.rs"] diff --git a/library/std/src/sys/pal/xous/net/dns.rs b/library/std/src/sys/pal/xous/net/dns.rs index 63056324bfbd9..50efe978c4a83 100644 --- a/library/std/src/sys/pal/xous/net/dns.rs +++ b/library/std/src/sys/pal/xous/net/dns.rs @@ -1,8 +1,9 @@ +use core::convert::{TryFrom, TryInto}; + use crate::io; use crate::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::os::xous::ffi::lend_mut; use crate::os::xous::services::{dns_server, DnsLendMut}; -use core::convert::{TryFrom, TryInto}; pub struct DnsError { pub code: u8, diff --git a/library/std/src/sys/pal/xous/net/tcplistener.rs b/library/std/src/sys/pal/xous/net/tcplistener.rs index 47305013083c8..ddfb289162b69 100644 --- a/library/std/src/sys/pal/xous/net/tcplistener.rs +++ b/library/std/src/sys/pal/xous/net/tcplistener.rs @@ -1,11 +1,11 @@ +use core::convert::TryInto; +use core::sync::atomic::{AtomicBool, AtomicU16, AtomicUsize, Ordering}; + use super::*; -use crate::fmt; -use crate::io; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use crate::os::xous::services; use crate::sync::Arc; -use core::convert::TryInto; -use core::sync::atomic::{AtomicBool, AtomicU16, AtomicUsize, Ordering}; +use crate::{fmt, io}; macro_rules! unimpl { () => { diff --git a/library/std/src/sys/pal/xous/net/tcpstream.rs b/library/std/src/sys/pal/xous/net/tcpstream.rs index 0ad8811071111..03442cf2fcdfd 100644 --- a/library/std/src/sys/pal/xous/net/tcpstream.rs +++ b/library/std/src/sys/pal/xous/net/tcpstream.rs @@ -1,3 +1,5 @@ +use core::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; + use super::*; use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; @@ -5,7 +7,6 @@ use crate::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAdd use crate::os::xous::services; use crate::sync::Arc; use crate::time::Duration; -use core::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; macro_rules! unimpl { () => { diff --git a/library/std/src/sys/pal/xous/net/udp.rs b/library/std/src/sys/pal/xous/net/udp.rs index 3d0522b25f3fb..de5133280ba9d 100644 --- a/library/std/src/sys/pal/xous/net/udp.rs +++ b/library/std/src/sys/pal/xous/net/udp.rs @@ -1,13 +1,13 @@ +use core::convert::TryInto; +use core::sync::atomic::{AtomicUsize, Ordering}; + use super::*; use crate::cell::Cell; -use crate::fmt; -use crate::io; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use crate::os::xous::services; use crate::sync::Arc; use crate::time::Duration; -use core::convert::TryInto; -use core::sync::atomic::{AtomicUsize, Ordering}; +use crate::{fmt, io}; macro_rules! unimpl { () => { diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index 8d2eaee8aa617..8f8f35428c487 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -1,11 +1,10 @@ use super::unsupported; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::io; use crate::marker::PhantomData; use crate::os::xous::ffi::Error as XousError; use crate::path::{self, PathBuf}; +use crate::{fmt, io}; #[cfg(not(test))] #[cfg(feature = "panic_unwind")] @@ -149,11 +148,11 @@ pub fn getenv(_: &OsStr) -> Option { None } -pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } -pub fn unsetenv(_: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs index da7d722cc7082..a95b0aa14d255 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/pal/xous/thread.rs @@ -1,3 +1,5 @@ +use core::arch::asm; + use crate::ffi::CStr; use crate::io; use crate::num::NonZero; @@ -7,7 +9,6 @@ use crate::os::xous::ffi::{ }; use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; use crate::time::Duration; -use core::arch::asm; pub struct Thread { tid: ThreadId, @@ -81,7 +82,7 @@ impl Thread { // Destroy TLS, which will free the TLS page and call the destructor for // any thread local storage (if any). unsafe { - crate::sys::thread_local_key::destroy_tls(); + crate::sys::thread_local::key::destroy_tls(); } // Deallocate the stack memory, along with the guard pages. Afterwards, diff --git a/library/std/src/sys/pal/xous/time.rs b/library/std/src/sys/pal/xous/time.rs index 4e4ae67efffa2..ae8be81c0b7c5 100644 --- a/library/std/src/sys/pal/xous/time.rs +++ b/library/std/src/sys/pal/xous/time.rs @@ -1,7 +1,7 @@ use crate::os::xous::ffi::blocking_scalar; -use crate::os::xous::services::{ - systime_server, ticktimer_server, SystimeScalar::GetUtcTimeMs, TicktimerScalar::ElapsedMs, -}; +use crate::os::xous::services::SystimeScalar::GetUtcTimeMs; +use crate::os::xous::services::TicktimerScalar::ElapsedMs; +use crate::os::xous::services::{systime_server, ticktimer_server}; use crate::time::Duration; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] diff --git a/library/std/src/sys/pal/zkvm/alloc.rs b/library/std/src/sys/pal/zkvm/alloc.rs index fd333f1215150..2fdca22352470 100644 --- a/library/std/src/sys/pal/zkvm/alloc.rs +++ b/library/std/src/sys/pal/zkvm/alloc.rs @@ -5,7 +5,7 @@ use crate::alloc::{GlobalAlloc, Layout, System}; unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - abi::sys_alloc_aligned(layout.size(), layout.align()) + unsafe { abi::sys_alloc_aligned(layout.size(), layout.align()) } } #[inline] diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 0b22eabca6d82..651f25d66236b 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -6,6 +6,7 @@ //! This is all super highly experimental and not actually intended for //! wide/production use yet, it's still all in the experimental category. This //! will likely change over time. +#![forbid(unsafe_op_in_unsafe_fn)] const WORD_SIZE: usize = core::mem::size_of::(); @@ -25,7 +26,6 @@ pub mod pipe; #[path = "../unsupported/process.rs"] pub mod process; pub mod stdio; -pub mod thread_local_key; #[path = "../unsupported/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index 759beb2d306b9..68d91a123acd4 100644 --- a/library/std/src/sys/pal/zkvm/os.rs +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -1,12 +1,11 @@ use super::{abi, unsupported, WORD_SIZE}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::io; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; use crate::sys::os_str; use crate::sys_common::FromInner; +use crate::{fmt, io}; pub fn errno() -> i32 { 0 @@ -115,11 +114,11 @@ pub fn getenv(varname: &OsStr) -> Option { Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() })) } -pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } -pub fn unsetenv(_: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } diff --git a/library/std/src/sys/pal/zkvm/stdio.rs b/library/std/src/sys/pal/zkvm/stdio.rs index e771ed0de28db..dd218c8894ca5 100644 --- a/library/std/src/sys/pal/zkvm/stdio.rs +++ b/library/std/src/sys/pal/zkvm/stdio.rs @@ -1,4 +1,5 @@ -use super::{abi, abi::fileno}; +use super::abi; +use super::abi::fileno; use crate::io; pub struct Stdin; diff --git a/library/std/src/sys/pal/zkvm/thread_local_key.rs b/library/std/src/sys/pal/zkvm/thread_local_key.rs deleted file mode 100644 index 2f67924c61823..0000000000000 --- a/library/std/src/sys/pal/zkvm/thread_local_key.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::alloc::{alloc, Layout}; - -pub type Key = usize; - -#[inline] -pub unsafe fn create(_dtor: Option) -> Key { - alloc(Layout::new::<*mut u8>()) as _ -} - -#[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - let key: *mut *mut u8 = core::ptr::with_exposed_provenance_mut(key); - *key = value; -} - -#[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - let key: *mut *mut u8 = core::ptr::with_exposed_provenance_mut(key); - *key -} - -#[inline] -pub unsafe fn destroy(_key: Key) {} diff --git a/library/std/src/sys/path/unix.rs b/library/std/src/sys/path/unix.rs index 837f68d3eaff7..2a7c025c3c46a 100644 --- a/library/std/src/sys/path/unix.rs +++ b/library/std/src/sys/path/unix.rs @@ -1,7 +1,6 @@ -use crate::env; use crate::ffi::OsStr; -use crate::io; use crate::path::{Path, PathBuf, Prefix}; +use crate::{env, io}; #[inline] pub fn is_sep_byte(b: u8) -> bool { diff --git a/library/std/src/sys/path/unsupported_backslash.rs b/library/std/src/sys/path/unsupported_backslash.rs index 7045c9be25b08..855f443678c6c 100644 --- a/library/std/src/sys/path/unsupported_backslash.rs +++ b/library/std/src/sys/path/unsupported_backslash.rs @@ -1,3 +1,4 @@ +#![forbid(unsafe_op_in_unsafe_fn)] use crate::ffi::OsStr; use crate::io; use crate::path::{Path, PathBuf, Prefix}; diff --git a/library/std/src/sys/path/windows.rs b/library/std/src/sys/path/windows.rs index cebc791023115..21841eb18cc0e 100644 --- a/library/std/src/sys/path/windows.rs +++ b/library/std/src/sys/path/windows.rs @@ -1,8 +1,7 @@ use crate::ffi::{OsStr, OsString}; -use crate::io; use crate::path::{Path, PathBuf, Prefix}; -use crate::ptr; use crate::sys::pal::{c, fill_utf16_buf, os2path, to_u16s}; +use crate::{io, ptr}; #[cfg(test)] mod tests; @@ -218,7 +217,7 @@ pub(crate) fn maybe_verbatim(path: &Path) -> io::Result> { get_long_path(path, true) } -/// Get a normalized absolute path that can bypass path length limits. +/// Gets a normalized absolute path that can bypass path length limits. /// /// Setting prefer_verbatim to true suggests a stronger preference for verbatim /// paths even when not strictly necessary. This allows the Windows API to avoid diff --git a/library/std/src/sys/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs index ff88ef4e0e1d0..c37c3e442aea6 100644 --- a/library/std/src/sys/personality/dwarf/eh.rs +++ b/library/std/src/sys/personality/dwarf/eh.rs @@ -12,9 +12,9 @@ #![allow(non_upper_case_globals)] #![allow(unused)] +use core::{mem, ptr}; + use super::DwarfReader; -use core::mem; -use core::ptr; pub const DW_EH_PE_omit: u8 = 0xFF; pub const DW_EH_PE_absptr: u8 = 0x00; @@ -70,45 +70,51 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result let func_start = context.func_start; let mut reader = DwarfReader::new(lsda); - - let start_encoding = reader.read::(); - // base address for landing pad offsets - let lpad_base = if start_encoding != DW_EH_PE_omit { - read_encoded_pointer(&mut reader, context, start_encoding)? - } else { - func_start + let lpad_base = unsafe { + let start_encoding = reader.read::(); + // base address for landing pad offsets + if start_encoding != DW_EH_PE_omit { + read_encoded_pointer(&mut reader, context, start_encoding)? + } else { + func_start + } }; + let call_site_encoding = unsafe { + let ttype_encoding = reader.read::(); + if ttype_encoding != DW_EH_PE_omit { + // Rust doesn't analyze exception types, so we don't care about the type table + reader.read_uleb128(); + } - let ttype_encoding = reader.read::(); - if ttype_encoding != DW_EH_PE_omit { - // Rust doesn't analyze exception types, so we don't care about the type table - reader.read_uleb128(); - } - - let call_site_encoding = reader.read::(); - let call_site_table_length = reader.read_uleb128(); - let action_table = reader.ptr.add(call_site_table_length as usize); + reader.read::() + }; + let action_table = unsafe { + let call_site_table_length = reader.read_uleb128(); + reader.ptr.add(call_site_table_length as usize) + }; let ip = context.ip; if !USING_SJLJ_EXCEPTIONS { // read the callsite table while reader.ptr < action_table { - // these are offsets rather than pointers; - let cs_start = read_encoded_offset(&mut reader, call_site_encoding)?; - let cs_len = read_encoded_offset(&mut reader, call_site_encoding)?; - let cs_lpad = read_encoded_offset(&mut reader, call_site_encoding)?; - let cs_action_entry = reader.read_uleb128(); - // Callsite table is sorted by cs_start, so if we've passed the ip, we - // may stop searching. - if ip < func_start.wrapping_add(cs_start) { - break; - } - if ip < func_start.wrapping_add(cs_start + cs_len) { - if cs_lpad == 0 { - return Ok(EHAction::None); - } else { - let lpad = lpad_base.wrapping_add(cs_lpad); - return Ok(interpret_cs_action(action_table, cs_action_entry, lpad)); + unsafe { + // these are offsets rather than pointers; + let cs_start = read_encoded_offset(&mut reader, call_site_encoding)?; + let cs_len = read_encoded_offset(&mut reader, call_site_encoding)?; + let cs_lpad = read_encoded_offset(&mut reader, call_site_encoding)?; + let cs_action_entry = reader.read_uleb128(); + // Callsite table is sorted by cs_start, so if we've passed the ip, we + // may stop searching. + if ip < func_start.wrapping_add(cs_start) { + break; + } + if ip < func_start.wrapping_add(cs_start + cs_len) { + if cs_lpad == 0 { + return Ok(EHAction::None); + } else { + let lpad = lpad_base.wrapping_add(cs_lpad); + return Ok(interpret_cs_action(action_table, cs_action_entry, lpad)); + } } } } @@ -125,15 +131,15 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result } let mut idx = ip.addr(); loop { - let cs_lpad = reader.read_uleb128(); - let cs_action_entry = reader.read_uleb128(); + let cs_lpad = unsafe { reader.read_uleb128() }; + let cs_action_entry = unsafe { reader.read_uleb128() }; idx -= 1; if idx == 0 { // Can never have null landing pad for sjlj -- that would have // been indicated by a -1 call site index. // FIXME(strict provenance) let lpad = ptr::with_exposed_provenance((cs_lpad + 1) as usize); - return Ok(interpret_cs_action(action_table, cs_action_entry, lpad)); + return Ok(unsafe { interpret_cs_action(action_table, cs_action_entry, lpad) }); } } } @@ -151,9 +157,9 @@ unsafe fn interpret_cs_action( } else { // If lpad != 0 and cs_action_entry != 0, we have to check ttype_index. // If ttype_index == 0 under the condition, we take cleanup action. - let action_record = action_table.offset(cs_action_entry as isize - 1); + let action_record = unsafe { action_table.offset(cs_action_entry as isize - 1) }; let mut action_reader = DwarfReader::new(action_record); - let ttype_index = action_reader.read_sleb128(); + let ttype_index = unsafe { action_reader.read_sleb128() }; if ttype_index == 0 { EHAction::Cleanup(lpad) } else if ttype_index > 0 { @@ -170,7 +176,7 @@ fn round_up(unrounded: usize, align: usize) -> Result { if align.is_power_of_two() { Ok((unrounded + align - 1) & !(align - 1)) } else { Err(()) } } -/// Read a offset (`usize`) from `reader` whose encoding is described by `encoding`. +/// Reads an offset (`usize`) from `reader` whose encoding is described by `encoding`. /// /// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext]. /// In addition the upper ("application") part must be zero. @@ -186,23 +192,25 @@ unsafe fn read_encoded_offset(reader: &mut DwarfReader, encoding: u8) -> Result< if encoding == DW_EH_PE_omit || encoding & 0xF0 != 0 { return Err(()); } - let result = match encoding & 0x0F { - // despite the name, LLVM also uses absptr for offsets instead of pointers - DW_EH_PE_absptr => reader.read::(), - DW_EH_PE_uleb128 => reader.read_uleb128() as usize, - DW_EH_PE_udata2 => reader.read::() as usize, - DW_EH_PE_udata4 => reader.read::() as usize, - DW_EH_PE_udata8 => reader.read::() as usize, - DW_EH_PE_sleb128 => reader.read_sleb128() as usize, - DW_EH_PE_sdata2 => reader.read::() as usize, - DW_EH_PE_sdata4 => reader.read::() as usize, - DW_EH_PE_sdata8 => reader.read::() as usize, - _ => return Err(()), + let result = unsafe { + match encoding & 0x0F { + // despite the name, LLVM also uses absptr for offsets instead of pointers + DW_EH_PE_absptr => reader.read::(), + DW_EH_PE_uleb128 => reader.read_uleb128() as usize, + DW_EH_PE_udata2 => reader.read::() as usize, + DW_EH_PE_udata4 => reader.read::() as usize, + DW_EH_PE_udata8 => reader.read::() as usize, + DW_EH_PE_sleb128 => reader.read_sleb128() as usize, + DW_EH_PE_sdata2 => reader.read::() as usize, + DW_EH_PE_sdata4 => reader.read::() as usize, + DW_EH_PE_sdata8 => reader.read::() as usize, + _ => return Err(()), + } }; Ok(result) } -/// Read a pointer from `reader` whose encoding is described by `encoding`. +/// Reads a pointer from `reader` whose encoding is described by `encoding`. /// /// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext]. /// @@ -250,14 +258,14 @@ unsafe fn read_encoded_pointer( if encoding & 0x0F != DW_EH_PE_absptr { return Err(()); } - reader.read::<*const u8>() + unsafe { reader.read::<*const u8>() } } else { - let offset = read_encoded_offset(reader, encoding & 0x0F)?; + let offset = unsafe { read_encoded_offset(reader, encoding & 0x0F)? }; base_ptr.wrapping_add(offset) }; if encoding & DW_EH_PE_indirect != 0 { - ptr = *(ptr.cast::<*const u8>()); + ptr = unsafe { *(ptr.cast::<*const u8>()) }; } Ok(ptr) diff --git a/library/std/src/sys/personality/dwarf/mod.rs b/library/std/src/sys/personality/dwarf/mod.rs index 652fbe95a14d1..5c52d96c4cad4 100644 --- a/library/std/src/sys/personality/dwarf/mod.rs +++ b/library/std/src/sys/personality/dwarf/mod.rs @@ -5,6 +5,7 @@ // This module is used only by x86_64-pc-windows-gnu for now, but we // are compiling it everywhere to avoid regressions. #![allow(unused)] +#![forbid(unsafe_op_in_unsafe_fn)] #[cfg(test)] mod tests; @@ -17,32 +18,29 @@ pub struct DwarfReader { pub ptr: *const u8, } -#[repr(C, packed)] -struct Unaligned(T); - impl DwarfReader { pub fn new(ptr: *const u8) -> DwarfReader { DwarfReader { ptr } } - // DWARF streams are packed, so e.g., a u32 would not necessarily be aligned - // on a 4-byte boundary. This may cause problems on platforms with strict - // alignment requirements. By wrapping data in a "packed" struct, we are - // telling the backend to generate "misalignment-safe" code. + /// Read a type T and then bump the pointer by that amount. + /// + /// DWARF streams are "packed", so all types must be read at align 1. pub unsafe fn read(&mut self) -> T { - let Unaligned(result) = *(self.ptr as *const Unaligned); - self.ptr = self.ptr.add(mem::size_of::()); - result + unsafe { + let result = self.ptr.cast::().read_unaligned(); + self.ptr = self.ptr.byte_add(mem::size_of::()); + result + } } - // ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable - // Length Data". + /// ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable Length Data". pub unsafe fn read_uleb128(&mut self) -> u64 { let mut shift: usize = 0; let mut result: u64 = 0; let mut byte: u8; loop { - byte = self.read::(); + byte = unsafe { self.read::() }; result |= ((byte & 0x7F) as u64) << shift; shift += 7; if byte & 0x80 == 0 { @@ -57,7 +55,7 @@ impl DwarfReader { let mut result: u64 = 0; let mut byte: u8; loop { - byte = self.read::(); + byte = unsafe { self.read::() }; result |= ((byte & 0x7F) as u64) << shift; shift += 7; if byte & 0x80 == 0 { diff --git a/library/std/src/sys/personality/emcc.rs b/library/std/src/sys/personality/emcc.rs index cb52ae89b1911..d374e3ad4c8f6 100644 --- a/library/std/src/sys/personality/emcc.rs +++ b/library/std/src/sys/personality/emcc.rs @@ -1,9 +1,10 @@ //! On Emscripten Rust panics are wrapped in C++ exceptions, so we just forward //! to `__gxx_personality_v0` which is provided by Emscripten. -use crate::ffi::c_int; use unwind as uw; +use crate::ffi::c_int; + // This is required by the compiler to exist (e.g., it's a lang item), but it's // never actually called by the compiler. Emscripten EH doesn't use a // personality function at all, it instead uses __cxa_find_matching_catch. diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 0dc53550ca943..f6b1844e153fd 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -35,10 +35,12 @@ //! //! Once stack has been unwound down to the handler frame level, unwinding stops //! and the last personality routine transfers control to the catch block. +#![forbid(unsafe_op_in_unsafe_fn)] + +use unwind as uw; use super::dwarf::eh::{self, EHAction, EHContext}; use crate::ffi::c_int; -use unwind as uw; // Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister() // and TargetLowering::getExceptionSelectorRegister() for each architecture, @@ -92,107 +94,116 @@ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c cfg_if::cfg_if! { - if #[cfg(all(not(all(target_vendor = "apple", not(target_os = "watchos"))), target_arch = "arm", not(target_os = "netbsd")))] { - // ARM EHABI personality routine. - // https://web.archive.org/web/20190728160938/https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf - // - // Apple 32-bit ARM (but not watchOS) uses the default routine instead - // since it uses SjLj unwinding. + if #[cfg(all( + target_arch = "arm", + not(all(target_vendor = "apple", not(target_os = "watchos"))), + not(target_os = "netbsd"), + ))] { + /// personality fn called by [ARM EHABI][armeabi-eh] + /// + /// Apple 32-bit ARM (but not watchOS) uses the default routine instead + /// since it uses "setjmp-longjmp" unwinding. + /// + /// [armeabi-eh]: https://web.archive.org/web/20190728160938/https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf #[lang = "eh_personality"] unsafe extern "C" fn rust_eh_personality( state: uw::_Unwind_State, exception_object: *mut uw::_Unwind_Exception, context: *mut uw::_Unwind_Context, ) -> uw::_Unwind_Reason_Code { - let state = state as c_int; - let action = state & uw::_US_ACTION_MASK as c_int; - let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { - // Backtraces on ARM will call the personality routine with - // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases - // we want to continue unwinding the stack, otherwise all our backtraces - // would end at __rust_try - if state & uw::_US_FORCE_UNWIND as c_int != 0 { + unsafe { + let state = state as c_int; + let action = state & uw::_US_ACTION_MASK as c_int; + let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { + // Backtraces on ARM will call the personality routine with + // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases + // we want to continue unwinding the stack, otherwise all our backtraces + // would end at __rust_try + if state & uw::_US_FORCE_UNWIND as c_int != 0 { + return continue_unwind(exception_object, context); + } + true + } else if action == uw::_US_UNWIND_FRAME_STARTING as c_int { + false + } else if action == uw::_US_UNWIND_FRAME_RESUME as c_int { return continue_unwind(exception_object, context); - } - true - } else if action == uw::_US_UNWIND_FRAME_STARTING as c_int { - false - } else if action == uw::_US_UNWIND_FRAME_RESUME as c_int { - return continue_unwind(exception_object, context); - } else { - return uw::_URC_FAILURE; - }; + } else { + return uw::_URC_FAILURE; + }; - // The DWARF unwinder assumes that _Unwind_Context holds things like the function - // and LSDA pointers, however ARM EHABI places them into the exception object. - // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which - // take only the context pointer, GCC personality routines stash a pointer to - // exception_object in the context, using location reserved for ARM's - // "scratch register" (r12). - uw::_Unwind_SetGR(context, uw::UNWIND_POINTER_REG, exception_object as uw::_Unwind_Ptr); - // ...A more principled approach would be to provide the full definition of ARM's - // _Unwind_Context in our libunwind bindings and fetch the required data from there - // directly, bypassing DWARF compatibility functions. + // The DWARF unwinder assumes that _Unwind_Context holds things like the function + // and LSDA pointers, however ARM EHABI places them into the exception object. + // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which + // take only the context pointer, GCC personality routines stash a pointer to + // exception_object in the context, using location reserved for ARM's + // "scratch register" (r12). + uw::_Unwind_SetGR(context, uw::UNWIND_POINTER_REG, exception_object as uw::_Unwind_Ptr); + // ...A more principled approach would be to provide the full definition of ARM's + // _Unwind_Context in our libunwind bindings and fetch the required data from there + // directly, bypassing DWARF compatibility functions. - let eh_action = match find_eh_action(context) { - Ok(action) => action, - Err(_) => return uw::_URC_FAILURE, - }; - if search_phase { - match eh_action { - EHAction::None | EHAction::Cleanup(_) => { - return continue_unwind(exception_object, context); - } - EHAction::Catch(_) | EHAction::Filter(_) => { - // EHABI requires the personality routine to update the - // SP value in the barrier cache of the exception object. - (*exception_object).private[5] = - uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG); - return uw::_URC_HANDLER_FOUND; + let eh_action = match find_eh_action(context) { + Ok(action) => action, + Err(_) => return uw::_URC_FAILURE, + }; + if search_phase { + match eh_action { + EHAction::None | EHAction::Cleanup(_) => { + return continue_unwind(exception_object, context); + } + EHAction::Catch(_) | EHAction::Filter(_) => { + // EHABI requires the personality routine to update the + // SP value in the barrier cache of the exception object. + (*exception_object).private[5] = + uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG); + return uw::_URC_HANDLER_FOUND; + } + EHAction::Terminate => return uw::_URC_FAILURE, } - EHAction::Terminate => return uw::_URC_FAILURE, - } - } else { - match eh_action { - EHAction::None => return continue_unwind(exception_object, context), - EHAction::Filter(_) if state & uw::_US_FORCE_UNWIND as c_int != 0 => return continue_unwind(exception_object, context), - EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => { - uw::_Unwind_SetGR( - context, - UNWIND_DATA_REG.0, - exception_object as uw::_Unwind_Ptr, - ); - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); - uw::_Unwind_SetIP(context, lpad); - return uw::_URC_INSTALL_CONTEXT; + } else { + match eh_action { + EHAction::None => return continue_unwind(exception_object, context), + EHAction::Filter(_) if state & uw::_US_FORCE_UNWIND as c_int != 0 => return continue_unwind(exception_object, context), + EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => { + uw::_Unwind_SetGR( + context, + UNWIND_DATA_REG.0, + exception_object as uw::_Unwind_Ptr, + ); + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); + uw::_Unwind_SetIP(context, lpad); + return uw::_URC_INSTALL_CONTEXT; + } + EHAction::Terminate => return uw::_URC_FAILURE, } - EHAction::Terminate => return uw::_URC_FAILURE, } - } - // On ARM EHABI the personality routine is responsible for actually - // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). - unsafe fn continue_unwind( - exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context, - ) -> uw::_Unwind_Reason_Code { - if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON { - uw::_URC_CONTINUE_UNWIND - } else { - uw::_URC_FAILURE - } - } - // defined in libgcc - extern "C" { - fn __gnu_unwind_frame( + // On ARM EHABI the personality routine is responsible for actually + // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). + unsafe fn continue_unwind( exception_object: *mut uw::_Unwind_Exception, context: *mut uw::_Unwind_Context, - ) -> uw::_Unwind_Reason_Code; + ) -> uw::_Unwind_Reason_Code { + unsafe { + if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON { + uw::_URC_CONTINUE_UNWIND + } else { + uw::_URC_FAILURE + } + } + } + // defined in libgcc + extern "C" { + fn __gnu_unwind_frame( + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context, + ) -> uw::_Unwind_Reason_Code; + } } } } else { - // Default personality routine, which is used directly on most targets - // and indirectly on Windows x86_64 via SEH. + /// Default personality routine, which is used directly on most targets + /// and indirectly on Windows x86_64 and AArch64 via SEH. unsafe extern "C" fn rust_eh_personality_impl( version: c_int, actions: uw::_Unwind_Action, @@ -200,43 +211,49 @@ cfg_if::cfg_if! { exception_object: *mut uw::_Unwind_Exception, context: *mut uw::_Unwind_Context, ) -> uw::_Unwind_Reason_Code { - if version != 1 { - return uw::_URC_FATAL_PHASE1_ERROR; - } - let eh_action = match find_eh_action(context) { - Ok(action) => action, - Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, - }; - if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { - match eh_action { - EHAction::None | EHAction::Cleanup(_) => uw::_URC_CONTINUE_UNWIND, - EHAction::Catch(_) | EHAction::Filter(_) => uw::_URC_HANDLER_FOUND, - EHAction::Terminate => uw::_URC_FATAL_PHASE1_ERROR, + unsafe { + if version != 1 { + return uw::_URC_FATAL_PHASE1_ERROR; } - } else { - match eh_action { - EHAction::None => uw::_URC_CONTINUE_UNWIND, - // Forced unwinding hits a terminate action. - EHAction::Filter(_) if actions as i32 & uw::_UA_FORCE_UNWIND as i32 != 0 => uw::_URC_CONTINUE_UNWIND, - EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => { - uw::_Unwind_SetGR( - context, - UNWIND_DATA_REG.0, - exception_object.cast(), - ); - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); - uw::_Unwind_SetIP(context, lpad); - uw::_URC_INSTALL_CONTEXT + let eh_action = match find_eh_action(context) { + Ok(action) => action, + Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, + }; + if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { + match eh_action { + EHAction::None | EHAction::Cleanup(_) => uw::_URC_CONTINUE_UNWIND, + EHAction::Catch(_) | EHAction::Filter(_) => uw::_URC_HANDLER_FOUND, + EHAction::Terminate => uw::_URC_FATAL_PHASE1_ERROR, + } + } else { + match eh_action { + EHAction::None => uw::_URC_CONTINUE_UNWIND, + // Forced unwinding hits a terminate action. + EHAction::Filter(_) if actions as i32 & uw::_UA_FORCE_UNWIND as i32 != 0 => uw::_URC_CONTINUE_UNWIND, + EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => { + uw::_Unwind_SetGR( + context, + UNWIND_DATA_REG.0, + exception_object.cast(), + ); + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); + uw::_Unwind_SetIP(context, lpad); + uw::_URC_INSTALL_CONTEXT + } + EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR, } - EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR, } } } cfg_if::cfg_if! { if #[cfg(all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), target_env = "gnu"))] { - // On x86_64 MinGW targets, the unwinding mechanism is SEH however the unwind - // handler data (aka LSDA) uses GCC-compatible encoding. + /// personality fn called by [Windows Structured Exception Handling][windows-eh] + /// + /// On x86_64 and AArch64 MinGW targets, the unwinding mechanism is SEH, + /// however the unwind handler data (aka LSDA) uses GCC-compatible encoding + /// + /// [windows-eh]: https://learn.microsoft.com/en-us/cpp/cpp/structured-exception-handling-c-cpp?view=msvc-170 #[lang = "eh_personality"] #[allow(nonstandard_style)] unsafe extern "C" fn rust_eh_personality( @@ -245,16 +262,33 @@ cfg_if::cfg_if! { contextRecord: *mut uw::CONTEXT, dispatcherContext: *mut uw::DISPATCHER_CONTEXT, ) -> uw::EXCEPTION_DISPOSITION { - uw::_GCC_specific_handler( - exceptionRecord, - establisherFrame, - contextRecord, - dispatcherContext, - rust_eh_personality_impl, - ) + // SAFETY: the cfg is still target_os = "windows" and target_env = "gnu", + // which means that this is the correct function to call, passing our impl fn + // as the callback which gets actually used + unsafe { + uw::_GCC_specific_handler( + exceptionRecord, + establisherFrame, + contextRecord, + dispatcherContext, + rust_eh_personality_impl, + ) + } } } else { - // The personality routine for most of our targets. + /// personality fn called by [Itanium C++ ABI Exception Handling][itanium-eh] + /// + /// The personality routine for most non-Windows targets. This will be called by + /// the unwinding library: + /// - "In the search phase, the framework repeatedly calls the personality routine, + /// with the _UA_SEARCH_PHASE flag as described below, first for the current PC + /// and register state, and then unwinding a frame to a new PC at each step..." + /// - "If the search phase reports success, the framework restarts in the cleanup + /// phase. Again, it repeatedly calls the personality routine, with the + /// _UA_CLEANUP_PHASE flag as described below, first for the current PC and + /// register state, and then unwinding a frame to a new PC at each step..."i + /// + /// [itanium-eh]: https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html #[lang = "eh_personality"] unsafe extern "C" fn rust_eh_personality( version: c_int, @@ -263,13 +297,17 @@ cfg_if::cfg_if! { exception_object: *mut uw::_Unwind_Exception, context: *mut uw::_Unwind_Context, ) -> uw::_Unwind_Reason_Code { - rust_eh_personality_impl( - version, - actions, - exception_class, - exception_object, - context, - ) + // SAFETY: the platform support must modify the cfg for the inner fn + // if it needs something different than what is currently invoked. + unsafe { + rust_eh_personality_impl( + version, + actions, + exception_class, + exception_object, + context, + ) + } } } } @@ -277,18 +315,20 @@ cfg_if::cfg_if! { } unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result { - let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; - let mut ip_before_instr: c_int = 0; - let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr); - let eh_context = EHContext { - // The return address points 1 byte past the call instruction, - // which could be in the next IP range in LSDA range table. - // - // `ip = -1` has special meaning, so use wrapping sub to allow for that - ip: if ip_before_instr != 0 { ip } else { ip.wrapping_sub(1) }, - func_start: uw::_Unwind_GetRegionStart(context), - get_text_start: &|| uw::_Unwind_GetTextRelBase(context), - get_data_start: &|| uw::_Unwind_GetDataRelBase(context), - }; - eh::find_eh_action(lsda, &eh_context) + unsafe { + let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; + let mut ip_before_instr: c_int = 0; + let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr); + let eh_context = EHContext { + // The return address points 1 byte past the call instruction, + // which could be in the next IP range in LSDA range table. + // + // `ip = -1` has special meaning, so use wrapping sub to allow for that + ip: if ip_before_instr != 0 { ip } else { ip.wrapping_sub(1) }, + func_start: uw::_Unwind_GetRegionStart(context), + get_text_start: &|| uw::_Unwind_GetTextRelBase(context), + get_data_start: &|| uw::_Unwind_GetDataRelBase(context), + }; + eh::find_eh_action(lsda, &eh_context) + } } diff --git a/library/std/src/sys/sync/condvar/futex.rs b/library/std/src/sys/sync/condvar/futex.rs index 4586d0fd941a7..39cd97c01ea32 100644 --- a/library/std/src/sys/sync/condvar/futex.rs +++ b/library/std/src/sys/sync/condvar/futex.rs @@ -1,4 +1,5 @@ -use crate::sync::atomic::{AtomicU32, Ordering::Relaxed}; +use crate::sync::atomic::AtomicU32; +use crate::sync::atomic::Ordering::Relaxed; use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; use crate::sys::sync::Mutex; use crate::time::Duration; diff --git a/library/std/src/sys/sync/condvar/itron.rs b/library/std/src/sys/sync/condvar/itron.rs index 9b64d241efd12..f6f2b698e4945 100644 --- a/library/std/src/sys/sync/condvar/itron.rs +++ b/library/std/src/sys/sync/condvar/itron.rs @@ -1,8 +1,13 @@ //! POSIX conditional variable implementation based on user-space wait queues. -use crate::sys::pal::itron::{ - abi, error::expect_success_aborting, spin::SpinMutex, task, time::with_tmos_strong, -}; -use crate::{mem::replace, ptr::NonNull, sys::sync::Mutex, time::Duration}; + +use crate::mem::replace; +use crate::ptr::NonNull; +use crate::sys::pal::itron::error::expect_success_aborting; +use crate::sys::pal::itron::spin::SpinMutex; +use crate::sys::pal::itron::time::with_tmos_strong; +use crate::sys::pal::itron::{abi, task}; +use crate::sys::sync::Mutex; +use crate::time::Duration; // The implementation is inspired by the queue-based implementation shown in // Andrew D. Birrell's paper "Implementing Condition Variables with Semaphores" diff --git a/library/std/src/sys/sync/condvar/pthread.rs b/library/std/src/sys/sync/condvar/pthread.rs index a2a96410d932c..2b4bdfe415c80 100644 --- a/library/std/src/sys/sync/condvar/pthread.rs +++ b/library/std/src/sys/sync/condvar/pthread.rs @@ -1,6 +1,7 @@ use crate::cell::UnsafeCell; use crate::ptr; -use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed}; +use crate::sync::atomic::AtomicPtr; +use crate::sync::atomic::Ordering::Relaxed; use crate::sys::sync::{mutex, Mutex}; #[cfg(not(target_os = "nto"))] use crate::sys::time::TIMESPEC_MAX; diff --git a/library/std/src/sys/sync/condvar/teeos.rs b/library/std/src/sys/sync/condvar/teeos.rs index 0a931f407d2fa..943867cd76169 100644 --- a/library/std/src/sys/sync/condvar/teeos.rs +++ b/library/std/src/sys/sync/condvar/teeos.rs @@ -1,6 +1,7 @@ use crate::cell::UnsafeCell; use crate::ptr; -use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed}; +use crate::sync::atomic::AtomicPtr; +use crate::sync::atomic::Ordering::Relaxed; use crate::sys::sync::mutex::{self, Mutex}; use crate::sys::time::TIMESPEC_MAX; use crate::sys_common::lazy_box::{LazyBox, LazyInit}; @@ -76,16 +77,16 @@ impl Condvar { #[inline] pub unsafe fn wait(&self, mutex: &Mutex) { - let mutex = mutex::raw(mutex); + let mutex = unsafe { mutex::raw(mutex) }; self.verify(mutex); - let r = libc::pthread_cond_wait(raw(self), mutex); + let r = unsafe { libc::pthread_cond_wait(raw(self), mutex) }; debug_assert_eq!(r, 0); } pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { use crate::sys::time::Timespec; - let mutex = mutex::raw(mutex); + let mutex = unsafe { mutex::raw(mutex) }; self.verify(mutex); let timeout = Timespec::now(libc::CLOCK_MONOTONIC) @@ -93,7 +94,7 @@ impl Condvar { .and_then(|t| t.to_timespec()) .unwrap_or(TIMESPEC_MAX); - let r = pthread_cond_timedwait(raw(self), mutex, &timeout); + let r = unsafe { pthread_cond_timedwait(raw(self), mutex, &timeout) }; assert!(r == libc::ETIMEDOUT || r == 0); r == 0 } diff --git a/library/std/src/sys/sync/condvar/windows7.rs b/library/std/src/sys/sync/condvar/windows7.rs index 07fa5fdd698ee..56eeeda551ebb 100644 --- a/library/std/src/sys/sync/condvar/windows7.rs +++ b/library/std/src/sys/sync/condvar/windows7.rs @@ -1,7 +1,6 @@ use crate::cell::UnsafeCell; -use crate::sys::c; -use crate::sys::os; use crate::sys::sync::{mutex, Mutex}; +use crate::sys::{c, os}; use crate::time::Duration; pub struct Condvar { diff --git a/library/std/src/sys/sync/condvar/xous.rs b/library/std/src/sys/sync/condvar/xous.rs index 7b218818ef8ef..007383479a100 100644 --- a/library/std/src/sys/sync/condvar/xous.rs +++ b/library/std/src/sys/sync/condvar/xous.rs @@ -1,8 +1,9 @@ +use core::sync::atomic::{AtomicUsize, Ordering}; + use crate::os::xous::ffi::{blocking_scalar, scalar}; use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; use crate::sys::sync::Mutex; use crate::time::Duration; -use core::sync::atomic::{AtomicUsize, Ordering}; // The implementation is inspired by Andrew D. Birrell's paper // "Implementing Condition Variables with Semaphores" diff --git a/library/std/src/sys/sync/mutex/fuchsia.rs b/library/std/src/sys/sync/mutex/fuchsia.rs index 5d89e5a13fd36..81a6361a83a49 100644 --- a/library/std/src/sys/sync/mutex/fuchsia.rs +++ b/library/std/src/sys/sync/mutex/fuchsia.rs @@ -37,10 +37,8 @@ //! //! [mutex in Fuchsia's libsync]: https://cs.opensource.google/fuchsia/fuchsia/+/main:zircon/system/ulib/sync/mutex.c -use crate::sync::atomic::{ - AtomicU32, - Ordering::{Acquire, Relaxed, Release}, -}; +use crate::sync::atomic::AtomicU32; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sys::futex::zircon::{ zx_futex_wait, zx_futex_wake_single_owner, zx_handle_t, zx_thread_self, ZX_ERR_BAD_HANDLE, ZX_ERR_BAD_STATE, ZX_ERR_INVALID_ARGS, ZX_ERR_TIMED_OUT, ZX_ERR_WRONG_TYPE, ZX_OK, diff --git a/library/std/src/sys/sync/mutex/futex.rs b/library/std/src/sys/sync/mutex/futex.rs index 7427cae94d68a..81afa94b14787 100644 --- a/library/std/src/sys/sync/mutex/futex.rs +++ b/library/std/src/sys/sync/mutex/futex.rs @@ -1,19 +1,8 @@ -use crate::sync::atomic::{ - self, - Ordering::{Acquire, Relaxed, Release}, -}; -use crate::sys::futex::{futex_wait, futex_wake}; - -cfg_if::cfg_if! { -if #[cfg(windows)] { - // On Windows we can have a smol futex - type Atomic = atomic::AtomicU8; - type State = u8; -} else { - type Atomic = atomic::AtomicU32; - type State = u32; -} -} +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sys::futex::{self, futex_wait, futex_wake}; + +type Atomic = futex::SmallAtomic; +type State = futex::SmallPrimitive; pub struct Mutex { futex: Atomic, diff --git a/library/std/src/sys/sync/mutex/itron.rs b/library/std/src/sys/sync/mutex/itron.rs index a134eb2d1beca..8440ffdd33772 100644 --- a/library/std/src/sys/sync/mutex/itron.rs +++ b/library/std/src/sys/sync/mutex/itron.rs @@ -1,17 +1,17 @@ //! Mutex implementation backed by μITRON mutexes. Assumes `acre_mtx` and //! `TA_INHERIT` are available. -use crate::sys::pal::itron::{ - abi, - error::{expect_success, expect_success_aborting, fail, ItronError}, - spin::SpinIdOnceCell, -}; +#![forbid(unsafe_op_in_unsafe_fn)] + +use crate::sys::pal::itron::abi; +use crate::sys::pal::itron::error::{expect_success, expect_success_aborting, fail, ItronError}; +use crate::sys::pal::itron::spin::SpinIdOnceCell; pub struct Mutex { /// The ID of the underlying mutex object mtx: SpinIdOnceCell<()>, } -/// Create a mutex object. This function never panics. +/// Creates a mutex object. This function never panics. fn new_mtx() -> Result { ItronError::err_if_negative(unsafe { abi::acre_mtx(&abi::T_CMTX { @@ -29,7 +29,7 @@ impl Mutex { Mutex { mtx: SpinIdOnceCell::new() } } - /// Get the inner mutex's ID, which is lazily created. + /// Gets the inner mutex's ID, which is lazily created. fn raw(&self) -> abi::ID { match self.mtx.get_or_try_init(|| new_mtx().map(|id| (id, ()))) { Ok((id, ())) => id, diff --git a/library/std/src/sys/sync/mutex/windows7.rs b/library/std/src/sys/sync/mutex/windows7.rs index ef2f84082cd5c..689dba10f01ed 100644 --- a/library/std/src/sys/sync/mutex/windows7.rs +++ b/library/std/src/sys/sync/mutex/windows7.rs @@ -25,7 +25,7 @@ unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} #[inline] -pub unsafe fn raw(m: &Mutex) -> c::PSRWLOCK { +pub unsafe fn raw(m: &Mutex) -> *mut c::SRWLOCK { m.srwlock.get() } diff --git a/library/std/src/sys/sync/mutex/xous.rs b/library/std/src/sys/sync/mutex/xous.rs index 1426e48f8b7af..63efa5a0210ab 100644 --- a/library/std/src/sys/sync/mutex/xous.rs +++ b/library/std/src/sys/sync/mutex/xous.rs @@ -1,9 +1,7 @@ use crate::os::xous::ffi::{blocking_scalar, do_yield}; use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; -use crate::sync::atomic::{ - AtomicBool, AtomicUsize, - Ordering::{Acquire, Relaxed, Release}, -}; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sync::atomic::{AtomicBool, AtomicUsize}; pub struct Mutex { /// The "locked" value indicates how many threads are waiting on this diff --git a/library/std/src/sys/sync/once/futex.rs b/library/std/src/sys/sync/once/futex.rs index 609085dcd4712..2c8a054282b01 100644 --- a/library/std/src/sys/sync/once/futex.rs +++ b/library/std/src/sys/sync/once/futex.rs @@ -1,14 +1,12 @@ use crate::cell::Cell; use crate::sync as public; -use crate::sync::atomic::{ - AtomicU32, - Ordering::{Acquire, Relaxed, Release}, -}; +use crate::sync::atomic::AtomicU32; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sync::once::ExclusiveState; use crate::sys::futex::{futex_wait, futex_wake_all}; // On some platforms, the OS is very nice and handles the waiter queue for us. -// This means we only need one atomic value with 5 states: +// This means we only need one atomic value with 4 states: /// No initialization has run yet, and no thread is currently using the Once. const INCOMPLETE: u32 = 0; @@ -19,16 +17,20 @@ const POISONED: u32 = 1; /// Some thread is currently attempting to run initialization. It may succeed, /// so all future threads need to wait for it to finish. const RUNNING: u32 = 2; -/// Some thread is currently attempting to run initialization and there are threads -/// waiting for it to finish. -const QUEUED: u32 = 3; /// Initialization has completed and all future calls should finish immediately. -const COMPLETE: u32 = 4; +const COMPLETE: u32 = 3; -// Threads wait by setting the state to QUEUED and calling `futex_wait` on the state +// An additional bit indicates whether there are waiting threads: + +/// May only be set if the state is not COMPLETE. +const QUEUED: u32 = 4; + +// Threads wait by setting the QUEUED bit and calling `futex_wait` on the state // variable. When the running thread finishes, it will wake all waiting threads using // `futex_wake_all`. +const STATE_MASK: u32 = 0b11; + pub struct OnceState { poisoned: bool, set_state_to: Cell, @@ -47,7 +49,7 @@ impl OnceState { } struct CompletionGuard<'a> { - state: &'a AtomicU32, + state_and_queued: &'a AtomicU32, set_state_on_drop_to: u32, } @@ -56,32 +58,32 @@ impl<'a> Drop for CompletionGuard<'a> { // Use release ordering to propagate changes to all threads checking // up on the Once. `futex_wake_all` does its own synchronization, hence // we do not need `AcqRel`. - if self.state.swap(self.set_state_on_drop_to, Release) == QUEUED { - futex_wake_all(&self.state); + if self.state_and_queued.swap(self.set_state_on_drop_to, Release) & QUEUED != 0 { + futex_wake_all(self.state_and_queued); } } } pub struct Once { - state: AtomicU32, + state_and_queued: AtomicU32, } impl Once { #[inline] pub const fn new() -> Once { - Once { state: AtomicU32::new(INCOMPLETE) } + Once { state_and_queued: AtomicU32::new(INCOMPLETE) } } #[inline] pub fn is_completed(&self) -> bool { // Use acquire ordering to make all initialization changes visible to the // current thread. - self.state.load(Acquire) == COMPLETE + self.state_and_queued.load(Acquire) == COMPLETE } #[inline] pub(crate) fn state(&mut self) -> ExclusiveState { - match *self.state.get_mut() { + match *self.state_and_queued.get_mut() { INCOMPLETE => ExclusiveState::Incomplete, POISONED => ExclusiveState::Poisoned, COMPLETE => ExclusiveState::Complete, @@ -89,31 +91,73 @@ impl Once { } } - // This uses FnMut to match the API of the generic implementation. As this - // implementation is quite light-weight, it is generic over the closure and - // so avoids the cost of dynamic dispatch. #[cold] #[track_caller] - pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) { - let mut state = self.state.load(Acquire); + pub fn wait(&self, ignore_poisoning: bool) { + let mut state_and_queued = self.state_and_queued.load(Acquire); loop { + let state = state_and_queued & STATE_MASK; + let queued = state_and_queued & QUEUED != 0; match state { + COMPLETE => return, + POISONED if !ignore_poisoning => { + // Panic to propagate the poison. + panic!("Once instance has previously been poisoned"); + } + _ => { + // Set the QUEUED bit if it has not already been set. + if !queued { + state_and_queued += QUEUED; + if let Err(new) = self.state_and_queued.compare_exchange_weak( + state, + state_and_queued, + Relaxed, + Acquire, + ) { + state_and_queued = new; + continue; + } + } + + futex_wait(&self.state_and_queued, state_and_queued, None); + state_and_queued = self.state_and_queued.load(Acquire); + } + } + } + } + + #[cold] + #[track_caller] + pub fn call(&self, ignore_poisoning: bool, f: &mut dyn FnMut(&public::OnceState)) { + let mut state_and_queued = self.state_and_queued.load(Acquire); + loop { + let state = state_and_queued & STATE_MASK; + let queued = state_and_queued & QUEUED != 0; + match state { + COMPLETE => return, POISONED if !ignore_poisoning => { // Panic to propagate the poison. panic!("Once instance has previously been poisoned"); } INCOMPLETE | POISONED => { // Try to register the current thread as the one running. - if let Err(new) = - self.state.compare_exchange_weak(state, RUNNING, Acquire, Acquire) - { - state = new; + let next = RUNNING + if queued { QUEUED } else { 0 }; + if let Err(new) = self.state_and_queued.compare_exchange_weak( + state_and_queued, + next, + Acquire, + Acquire, + ) { + state_and_queued = new; continue; } + // `waiter_queue` will manage other waiting threads, and // wake them up on drop. - let mut waiter_queue = - CompletionGuard { state: &self.state, set_state_on_drop_to: POISONED }; + let mut waiter_queue = CompletionGuard { + state_and_queued: &self.state_and_queued, + set_state_on_drop_to: POISONED, + }; // Run the function, letting it know if we're poisoned or not. let f_state = public::OnceState { inner: OnceState { @@ -125,21 +169,27 @@ impl Once { waiter_queue.set_state_on_drop_to = f_state.inner.set_state_to.get(); return; } - RUNNING | QUEUED => { - // Set the state to QUEUED if it is not already. - if state == RUNNING - && let Err(new) = - self.state.compare_exchange_weak(RUNNING, QUEUED, Relaxed, Acquire) - { - state = new; - continue; + _ => { + // All other values must be RUNNING. + assert!(state == RUNNING); + + // Set the QUEUED bit if it is not already set. + if !queued { + state_and_queued += QUEUED; + if let Err(new) = self.state_and_queued.compare_exchange_weak( + state, + state_and_queued, + Relaxed, + Acquire, + ) { + state_and_queued = new; + continue; + } } - futex_wait(&self.state, QUEUED, None); - state = self.state.load(Acquire); + futex_wait(&self.state_and_queued, state_and_queued, None); + state_and_queued = self.state_and_queued.load(Acquire); } - COMPLETE => return, - _ => unreachable!("state is never set to invalid values"), } } } diff --git a/library/std/src/sys/sync/once/mod.rs b/library/std/src/sys/sync/once/mod.rs index 61b29713fa1a9..0e38937b1219a 100644 --- a/library/std/src/sys/sync/once/mod.rs +++ b/library/std/src/sys/sync/once/mod.rs @@ -9,6 +9,7 @@ cfg_if::cfg_if! { if #[cfg(any( + all(target_os = "windows", not(target_vendor="win7")), target_os = "linux", target_os = "android", all(target_arch = "wasm32", target_feature = "atomics"), diff --git a/library/std/src/sys/sync/once/no_threads.rs b/library/std/src/sys/sync/once/no_threads.rs index 11fde1888ba7c..12c1d9f5a6c98 100644 --- a/library/std/src/sys/sync/once/no_threads.rs +++ b/library/std/src/sys/sync/once/no_threads.rs @@ -55,6 +55,12 @@ impl Once { } } + #[cold] + #[track_caller] + pub fn wait(&self, _ignore_poisoning: bool) { + panic!("not implementable on this target"); + } + #[cold] #[track_caller] pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) { diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs index 730cdb768bd27..86f72c82008bc 100644 --- a/library/std/src/sys/sync/once/queue.rs +++ b/library/std/src/sys/sync/once/queue.rs @@ -56,22 +56,21 @@ // allowed, so no need for `SeqCst`. use crate::cell::Cell; -use crate::fmt; -use crate::ptr; -use crate::sync as public; -use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; +use crate::sync::atomic::Ordering::{AcqRel, Acquire, Release}; +use crate::sync::atomic::{AtomicBool, AtomicPtr}; use crate::sync::once::ExclusiveState; use crate::thread::{self, Thread}; +use crate::{fmt, ptr, sync as public}; -type Masked = (); +type StateAndQueue = *mut (); pub struct Once { - state_and_queue: AtomicPtr, + state_and_queue: AtomicPtr<()>, } pub struct OnceState { poisoned: bool, - set_state_on_drop_to: Cell<*mut Masked>, + set_state_on_drop_to: Cell, } // Four states that a Once can be in, encoded into the lower bits of @@ -83,7 +82,8 @@ const COMPLETE: usize = 0x3; // Mask to learn about the state. All other bits are the queue of waiters if // this is in the RUNNING state. -const STATE_MASK: usize = 0x3; +const STATE_MASK: usize = 0b11; +const QUEUE_MASK: usize = !STATE_MASK; // Representation of a node in the linked list of waiters, used while in the // RUNNING state. @@ -95,15 +95,23 @@ const STATE_MASK: usize = 0x3; struct Waiter { thread: Cell>, signaled: AtomicBool, - next: *const Waiter, + next: Cell<*const Waiter>, } // Head of a linked list of waiters. // Every node is a struct on the stack of a waiting thread. // Will wake up the waiters when it gets dropped, i.e. also on panic. struct WaiterQueue<'a> { - state_and_queue: &'a AtomicPtr, - set_state_on_drop_to: *mut Masked, + state_and_queue: &'a AtomicPtr<()>, + set_state_on_drop_to: StateAndQueue, +} + +fn to_queue(current: StateAndQueue) -> *const Waiter { + current.mask(QUEUE_MASK).cast() +} + +fn to_state(current: StateAndQueue) -> usize { + current.addr() & STATE_MASK } impl Once { @@ -119,7 +127,7 @@ impl Once { // operations visible to us, and, this being a fast path, weaker // ordering helps with performance. This `Acquire` synchronizes with // `Release` operations on the slow path. - self.state_and_queue.load(Ordering::Acquire).addr() == COMPLETE + self.state_and_queue.load(Acquire).addr() == COMPLETE } #[inline] @@ -132,6 +140,25 @@ impl Once { } } + #[cold] + #[track_caller] + pub fn wait(&self, ignore_poisoning: bool) { + let mut current = self.state_and_queue.load(Acquire); + loop { + let state = to_state(current); + match state { + COMPLETE => return, + POISONED if !ignore_poisoning => { + // Panic to propagate the poison. + panic!("Once instance has previously been poisoned"); + } + _ => { + current = wait(&self.state_and_queue, current, !ignore_poisoning); + } + } + } + } + // This is a non-generic function to reduce the monomorphization cost of // using `call_once` (this isn't exactly a trivial or small implementation). // @@ -146,9 +173,10 @@ impl Once { #[cold] #[track_caller] pub fn call(&self, ignore_poisoning: bool, init: &mut dyn FnMut(&public::OnceState)) { - let mut state_and_queue = self.state_and_queue.load(Ordering::Acquire); + let mut current = self.state_and_queue.load(Acquire); loop { - match state_and_queue.addr() { + let state = to_state(current); + match state { COMPLETE => break, POISONED if !ignore_poisoning => { // Panic to propagate the poison. @@ -156,16 +184,16 @@ impl Once { } POISONED | INCOMPLETE => { // Try to register this thread as the one RUNNING. - let exchange_result = self.state_and_queue.compare_exchange( - state_and_queue, - ptr::without_provenance_mut(RUNNING), - Ordering::Acquire, - Ordering::Acquire, - ); - if let Err(old) = exchange_result { - state_and_queue = old; + if let Err(new) = self.state_and_queue.compare_exchange_weak( + current, + current.mask(QUEUE_MASK).wrapping_byte_add(RUNNING), + Acquire, + Acquire, + ) { + current = new; continue; } + // `waiter_queue` will manage other waiting threads, and // wake them up on drop. let mut waiter_queue = WaiterQueue { @@ -176,54 +204,57 @@ impl Once { // poisoned or not. let init_state = public::OnceState { inner: OnceState { - poisoned: state_and_queue.addr() == POISONED, + poisoned: state == POISONED, set_state_on_drop_to: Cell::new(ptr::without_provenance_mut(COMPLETE)), }, }; init(&init_state); waiter_queue.set_state_on_drop_to = init_state.inner.set_state_on_drop_to.get(); - break; + return; } _ => { // All other values must be RUNNING with possibly a // pointer to the waiter queue in the more significant bits. - assert!(state_and_queue.addr() & STATE_MASK == RUNNING); - wait(&self.state_and_queue, state_and_queue); - state_and_queue = self.state_and_queue.load(Ordering::Acquire); + assert!(state == RUNNING); + current = wait(&self.state_and_queue, current, true); } } } } } -fn wait(state_and_queue: &AtomicPtr, mut current_state: *mut Masked) { - // Note: the following code was carefully written to avoid creating a - // mutable reference to `node` that gets aliased. +fn wait( + state_and_queue: &AtomicPtr<()>, + mut current: StateAndQueue, + return_on_poisoned: bool, +) -> StateAndQueue { + let node = &Waiter { + thread: Cell::new(Some(thread::current())), + signaled: AtomicBool::new(false), + next: Cell::new(ptr::null()), + }; + loop { - // Don't queue this thread if the status is no longer running, - // otherwise we will not be woken up. - if current_state.addr() & STATE_MASK != RUNNING { - return; + let state = to_state(current); + let queue = to_queue(current); + + // If initialization has finished, return. + if state == COMPLETE || (return_on_poisoned && state == POISONED) { + return current; } - // Create the node for our current thread. - let node = Waiter { - thread: Cell::new(Some(thread::current())), - signaled: AtomicBool::new(false), - next: current_state.with_addr(current_state.addr() & !STATE_MASK) as *const Waiter, - }; - let me = core::ptr::addr_of!(node) as *const Masked as *mut Masked; + // Update the node for our current thread. + node.next.set(queue); // Try to slide in the node at the head of the linked list, making sure // that another thread didn't just replace the head of the linked list. - let exchange_result = state_and_queue.compare_exchange( - current_state, - me.with_addr(me.addr() | RUNNING), - Ordering::Release, - Ordering::Relaxed, - ); - if let Err(old) = exchange_result { - current_state = old; + if let Err(new) = state_and_queue.compare_exchange_weak( + current, + ptr::from_ref(node).wrapping_byte_add(state) as StateAndQueue, + Release, + Acquire, + ) { + current = new; continue; } @@ -232,14 +263,15 @@ fn wait(state_and_queue: &AtomicPtr, mut current_state: *mut Masked) { // would drop our `Waiter` node and leave a hole in the linked list // (and a dangling reference). Guard against spurious wakeups by // reparking ourselves until we are signaled. - while !node.signaled.load(Ordering::Acquire) { + while !node.signaled.load(Acquire) { // If the managing thread happens to signal and unpark us before we // can park ourselves, the result could be this thread never gets // unparked. Luckily `park` comes with the guarantee that if it got // an `unpark` just before on an unparked thread it does not park. thread::park(); } - break; + + return state_and_queue.load(Acquire); } } @@ -253,11 +285,10 @@ impl fmt::Debug for Once { impl Drop for WaiterQueue<'_> { fn drop(&mut self) { // Swap out our state with however we finished. - let state_and_queue = - self.state_and_queue.swap(self.set_state_on_drop_to, Ordering::AcqRel); + let current = self.state_and_queue.swap(self.set_state_on_drop_to, AcqRel); // We should only ever see an old state which was RUNNING. - assert_eq!(state_and_queue.addr() & STATE_MASK, RUNNING); + assert_eq!(current.addr() & STATE_MASK, RUNNING); // Walk the entire linked list of waiters and wake them up (in lifo // order, last to register is first to wake up). @@ -266,16 +297,13 @@ impl Drop for WaiterQueue<'_> { // free `node` if there happens to be has a spurious wakeup. // So we have to take out the `thread` field and copy the pointer to // `next` first. - let mut queue = - state_and_queue.with_addr(state_and_queue.addr() & !STATE_MASK) as *const Waiter; + let mut queue = to_queue(current); while !queue.is_null() { - let next = (*queue).next; + let next = (*queue).next.get(); let thread = (*queue).thread.take().unwrap(); - (*queue).signaled.store(true, Ordering::Release); - // ^- FIXME (maybe): This is another case of issue #55005 - // `store()` has a potentially dangling ref to `signaled`. - queue = next; + (*queue).signaled.store(true, Release); thread.unpark(); + queue = next; } } } diff --git a/library/std/src/sys/sync/rwlock/futex.rs b/library/std/src/sys/sync/rwlock/futex.rs index aa0de900238f5..75ecc2ab5c52f 100644 --- a/library/std/src/sys/sync/rwlock/futex.rs +++ b/library/std/src/sys/sync/rwlock/futex.rs @@ -1,7 +1,5 @@ -use crate::sync::atomic::{ - AtomicU32, - Ordering::{Acquire, Relaxed, Release}, -}; +use crate::sync::atomic::AtomicU32; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; pub struct RwLock { @@ -222,7 +220,7 @@ impl RwLock { } } - /// Wake up waiting threads after unlocking. + /// Wakes up waiting threads after unlocking. /// /// If both are waiting, this will wake up only one writer, but will fall /// back to waking up readers if there was no writer to wake up. diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs index 337cc6c2ca094..458c16516bbe1 100644 --- a/library/std/src/sys/sync/rwlock/queue.rs +++ b/library/std/src/sys/sync/rwlock/queue.rs @@ -111,10 +111,8 @@ use crate::cell::OnceCell; use crate::hint::spin_loop; use crate::mem; use crate::ptr::{self, null_mut, without_provenance_mut, NonNull}; -use crate::sync::atomic::{ - AtomicBool, AtomicPtr, - Ordering::{AcqRel, Acquire, Relaxed, Release}, -}; +use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release}; +use crate::sync::atomic::{AtomicBool, AtomicPtr}; use crate::thread::{self, Thread}; // Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the @@ -186,7 +184,7 @@ struct Node { } impl Node { - /// Create a new queue node. + /// Creates a new queue node. fn new(write: bool) -> Node { Node { next: AtomicLink::new(None), diff --git a/library/std/src/sys/sync/rwlock/solid.rs b/library/std/src/sys/sync/rwlock/solid.rs index 9bf6f5dbb731e..0537140202091 100644 --- a/library/std/src/sys/sync/rwlock/solid.rs +++ b/library/std/src/sys/sync/rwlock/solid.rs @@ -1,11 +1,9 @@ //! A readers-writer lock implementation backed by the SOLID kernel extension. -use crate::sys::pal::{ - abi, - itron::{ - error::{expect_success, expect_success_aborting, fail, ItronError}, - spin::SpinIdOnceCell, - }, -}; +#![forbid(unsafe_op_in_unsafe_fn)] + +use crate::sys::pal::abi; +use crate::sys::pal::itron::error::{expect_success, expect_success_aborting, fail, ItronError}; +use crate::sys::pal::itron::spin::SpinIdOnceCell; pub struct RwLock { /// The ID of the underlying mutex object @@ -26,7 +24,7 @@ impl RwLock { RwLock { rwl: SpinIdOnceCell::new() } } - /// Get the inner mutex's ID, which is lazily created. + /// Gets the inner mutex's ID, which is lazily created. fn raw(&self) -> abi::ID { match self.rwl.get_or_try_init(|| new_rwl().map(|id| (id, ()))) { Ok((id, ())) => id, diff --git a/library/std/src/sys/sync/thread_parking/darwin.rs b/library/std/src/sys/sync/thread_parking/darwin.rs index 973c08f03171e..96e3d23c332c4 100644 --- a/library/std/src/sys/sync/thread_parking/darwin.rs +++ b/library/std/src/sys/sync/thread_parking/darwin.rs @@ -13,10 +13,8 @@ #![allow(non_camel_case_types)] use crate::pin::Pin; -use crate::sync::atomic::{ - AtomicI8, - Ordering::{Acquire, Release}, -}; +use crate::sync::atomic::AtomicI8; +use crate::sync::atomic::Ordering::{Acquire, Release}; use crate::time::Duration; type dispatch_semaphore_t = *mut crate::ffi::c_void; diff --git a/library/std/src/sys/sync/thread_parking/futex.rs b/library/std/src/sys/sync/thread_parking/futex.rs index 588e7b27826f6..ce852eaadc4d9 100644 --- a/library/std/src/sys/sync/thread_parking/futex.rs +++ b/library/std/src/sys/sync/thread_parking/futex.rs @@ -1,15 +1,18 @@ +#![forbid(unsafe_op_in_unsafe_fn)] use crate::pin::Pin; -use crate::sync::atomic::AtomicU32; use crate::sync::atomic::Ordering::{Acquire, Release}; -use crate::sys::futex::{futex_wait, futex_wake}; +use crate::sys::futex::{self, futex_wait, futex_wake}; use crate::time::Duration; -const PARKED: u32 = u32::MAX; -const EMPTY: u32 = 0; -const NOTIFIED: u32 = 1; +type Atomic = futex::SmallAtomic; +type State = futex::SmallPrimitive; + +const PARKED: State = State::MAX; +const EMPTY: State = 0; +const NOTIFIED: State = 1; pub struct Parker { - state: AtomicU32, + state: Atomic, } // Notes about memory ordering: @@ -33,10 +36,10 @@ pub struct Parker { // Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using // Ordering::Acquire when checking for this state in park(). impl Parker { - /// Construct the futex parker. The UNIX parker implementation + /// Constructs the futex parker. The UNIX parker implementation /// requires this to happen in-place. pub unsafe fn new_in_place(parker: *mut Parker) { - parker.write(Self { state: AtomicU32::new(EMPTY) }); + unsafe { parker.write(Self { state: Atomic::new(EMPTY) }) }; } // Assumes this is only called by the thread that owns the Parker, diff --git a/library/std/src/sys/sync/thread_parking/id.rs b/library/std/src/sys/sync/thread_parking/id.rs index 0466743966034..a7b07b509dfd8 100644 --- a/library/std/src/sys/sync/thread_parking/id.rs +++ b/library/std/src/sys/sync/thread_parking/id.rs @@ -9,10 +9,8 @@ use crate::cell::UnsafeCell; use crate::pin::Pin; -use crate::sync::atomic::{ - fence, AtomicI8, - Ordering::{Acquire, Relaxed, Release}, -}; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sync::atomic::{fence, AtomicI8}; use crate::sys::thread_parking::{current, park, park_timeout, unpark, ThreadId}; use crate::time::Duration; @@ -30,7 +28,7 @@ impl Parker { Parker { state: AtomicI8::new(EMPTY), tid: UnsafeCell::new(None) } } - /// Create a new thread parker. UNIX requires this to happen in-place. + /// Creates a new thread parker. UNIX requires this to happen in-place. pub unsafe fn new_in_place(parker: *mut Parker) { parker.write(Parker::new()) } diff --git a/library/std/src/sys/sync/thread_parking/mod.rs b/library/std/src/sys/sync/thread_parking/mod.rs index ed1a6437faaaf..0ebc5e093ee2a 100644 --- a/library/std/src/sys/sync/thread_parking/mod.rs +++ b/library/std/src/sys/sync/thread_parking/mod.rs @@ -1,5 +1,6 @@ cfg_if::cfg_if! { if #[cfg(any( + all(target_os = "windows", not(target_vendor = "win7")), target_os = "linux", target_os = "android", all(target_arch = "wasm32", target_feature = "atomics"), @@ -18,9 +19,9 @@ cfg_if::cfg_if! { ))] { mod id; pub use id::Parker; - } else if #[cfg(target_os = "windows")] { - mod windows; - pub use windows::Parker; + } else if #[cfg(target_vendor = "win7")] { + mod windows7; + pub use windows7::Parker; } else if #[cfg(all(target_vendor = "apple", not(miri)))] { mod darwin; pub use darwin::Parker; diff --git a/library/std/src/sys/sync/thread_parking/pthread.rs b/library/std/src/sys/sync/thread_parking/pthread.rs index fdac1096dbfc1..c64600e9e29c3 100644 --- a/library/std/src/sys/sync/thread_parking/pthread.rs +++ b/library/std/src/sys/sync/thread_parking/pthread.rs @@ -92,7 +92,7 @@ pub struct Parker { } impl Parker { - /// Construct the UNIX parker in-place. + /// Constructs the UNIX parker in-place. /// /// # Safety /// The constructed parker must never be moved. diff --git a/library/std/src/sys/sync/thread_parking/windows.rs b/library/std/src/sys/sync/thread_parking/windows7.rs similarity index 95% rename from library/std/src/sys/sync/thread_parking/windows.rs rename to library/std/src/sys/sync/thread_parking/windows7.rs index 4b8102d505a1f..1000b63b6d01a 100644 --- a/library/std/src/sys/sync/thread_parking/windows.rs +++ b/library/std/src/sys/sync/thread_parking/windows7.rs @@ -57,11 +57,11 @@ // [3]: https://docs.microsoft.com/en-us/archive/msdn-magazine/2012/november/windows-with-c-the-evolution-of-synchronization-in-windows-and-c // [4]: Windows Internals, Part 1, ISBN 9780735671300 +use core::ffi::c_void; + use crate::pin::Pin; -use crate::sync::atomic::{ - AtomicI8, - Ordering::{Acquire, Release}, -}; +use crate::sync::atomic::AtomicI8; +use crate::sync::atomic::Ordering::{Acquire, Release}; use crate::sys::{c, dur2timeout}; use crate::time::Duration; @@ -94,7 +94,7 @@ const NOTIFIED: i8 = 1; // Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using // Ordering::Acquire when reading this state in park() after waking up. impl Parker { - /// Construct the Windows parker. The UNIX parker implementation + /// Constructs the Windows parker. The UNIX parker implementation /// requires this to happen in-place. pub unsafe fn new_in_place(parker: *mut Parker) { parker.write(Self { state: AtomicI8::new(EMPTY) }); @@ -117,7 +117,7 @@ impl Parker { loop { // Wait for something to happen, assuming it's still set to PARKED. - c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE); + c::WaitOnAddress(self.ptr(), &PARKED as *const _ as *const c_void, 1, c::INFINITE); // Change NOTIFIED=>EMPTY but leave PARKED alone. if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire).is_ok() { // Actually woken up by unpark(). @@ -144,7 +144,7 @@ impl Parker { } // Wait for something to happen, assuming it's still set to PARKED. - c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout)); + c::WaitOnAddress(self.ptr(), &PARKED as *const _ as *const c_void, 1, dur2timeout(timeout)); // Set the state back to EMPTY (from either PARKED or NOTIFIED). // Note that we don't just write EMPTY, but use swap() to also // include an acquire-ordered read to synchronize with unpark()'s @@ -177,23 +177,22 @@ impl Parker { } } - fn ptr(&self) -> c::LPVOID { - core::ptr::addr_of!(self.state) as c::LPVOID + fn ptr(&self) -> *const c_void { + core::ptr::addr_of!(self.state).cast::() } } #[cfg(target_vendor = "win7")] mod keyed_events { - use super::{Parker, EMPTY, NOTIFIED}; - use crate::sys::c; use core::pin::Pin; use core::ptr; - use core::sync::atomic::{ - AtomicPtr, - Ordering::{Acquire, Relaxed}, - }; + use core::sync::atomic::AtomicPtr; + use core::sync::atomic::Ordering::{Acquire, Relaxed}; use core::time::Duration; + use super::{Parker, EMPTY, NOTIFIED}; + use crate::sys::c; + pub unsafe fn park(parker: Pin<&Parker>) { // Wait for unpark() to produce this event. c::NtWaitForKeyedEvent(keyed_event_handle(), parker.ptr(), 0, ptr::null_mut()); diff --git a/library/std/src/sys/sync/thread_parking/xous.rs b/library/std/src/sys/sync/thread_parking/xous.rs index 0bd0462d77d35..64b6f731f2377 100644 --- a/library/std/src/sys/sync/thread_parking/xous.rs +++ b/library/std/src/sys/sync/thread_parking/xous.rs @@ -2,10 +2,8 @@ use crate::os::xous::ffi::{blocking_scalar, scalar}; use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; use crate::pin::Pin; use crate::ptr; -use crate::sync::atomic::{ - AtomicI8, - Ordering::{Acquire, Release}, -}; +use crate::sync::atomic::AtomicI8; +use crate::sync::atomic::Ordering::{Acquire, Release}; use crate::time::Duration; const NOTIFIED: i8 = 1; diff --git a/library/std/src/sys/thread_local/destructors/linux_like.rs b/library/std/src/sys/thread_local/destructors/linux_like.rs new file mode 100644 index 0000000000000..c381be0bf8c76 --- /dev/null +++ b/library/std/src/sys/thread_local/destructors/linux_like.rs @@ -0,0 +1,58 @@ +//! Destructor registration for Linux-like systems. +//! +//! Since what appears to be version 2.18, glibc has shipped the +//! `__cxa_thread_atexit_impl` symbol which GCC and clang both use to invoke +//! destructors in C++ thread_local globals. This function does exactly what +//! we want: it schedules a callback which will be run at thread exit with the +//! provided argument. +//! +//! Unfortunately, our minimum supported glibc version (at the time of writing) +//! is 2.17, so we can only link this symbol weakly and need to use the +//! [`list`](super::list) destructor implementation as fallback. + +use crate::mem::transmute; + +// FIXME: The Rust compiler currently omits weakly function definitions (i.e., +// __cxa_thread_atexit_impl) and its metadata from LLVM IR. +#[no_sanitize(cfi, kcfi)] +pub unsafe fn register(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + /// This is necessary because the __cxa_thread_atexit_impl implementation + /// std links to by default may be a C or C++ implementation that was not + /// compiled using the Clang integer normalization option. + #[cfg(sanitizer_cfi_normalize_integers)] + use core::ffi::c_int; + #[cfg(not(sanitizer_cfi_normalize_integers))] + #[cfi_encoding = "i"] + #[repr(transparent)] + #[allow(non_camel_case_types)] + pub struct c_int(#[allow(dead_code)] pub core::ffi::c_int); + + extern "C" { + #[linkage = "extern_weak"] + static __dso_handle: *mut u8; + #[linkage = "extern_weak"] + static __cxa_thread_atexit_impl: Option< + extern "C" fn( + unsafe extern "C" fn(*mut libc::c_void), + *mut libc::c_void, + *mut libc::c_void, + ) -> c_int, + >; + } + + if let Some(f) = unsafe { __cxa_thread_atexit_impl } { + unsafe { + f( + transmute::( + dtor, + ), + t.cast(), + core::ptr::addr_of!(__dso_handle) as *mut _, + ); + } + } else { + unsafe { + super::list::register(t, dtor); + } + } +} diff --git a/library/std/src/sys/thread_local/destructors/list.rs b/library/std/src/sys/thread_local/destructors/list.rs new file mode 100644 index 0000000000000..b9d5214c438d2 --- /dev/null +++ b/library/std/src/sys/thread_local/destructors/list.rs @@ -0,0 +1,44 @@ +use crate::cell::RefCell; +use crate::sys::thread_local::guard; + +#[thread_local] +static DTORS: RefCell> = RefCell::new(Vec::new()); + +pub unsafe fn register(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + let Ok(mut dtors) = DTORS.try_borrow_mut() else { + // This point can only be reached if the global allocator calls this + // function again. + // FIXME: maybe use the system allocator instead? + rtabort!("the global allocator may not use TLS with destructors"); + }; + + guard::enable(); + + dtors.push((t, dtor)); +} + +/// The [`guard`] module contains platform-specific functions which will run this +/// function on thread exit if [`guard::enable`] has been called. +/// +/// # Safety +/// +/// May only be run on thread exit to guarantee that there are no live references +/// to TLS variables while they are destroyed. +pub unsafe fn run() { + loop { + let mut dtors = DTORS.borrow_mut(); + match dtors.pop() { + Some((t, dtor)) => { + drop(dtors); + unsafe { + dtor(t); + } + } + None => { + // Free the list memory. + *dtors = Vec::new(); + break; + } + } + } +} diff --git a/library/std/src/sys/thread_local/guard/apple.rs b/library/std/src/sys/thread_local/guard/apple.rs new file mode 100644 index 0000000000000..6c27f7ae35cba --- /dev/null +++ b/library/std/src/sys/thread_local/guard/apple.rs @@ -0,0 +1,31 @@ +//! macOS allows registering destructors through _tlv_atexit. But since calling +//! it while TLS destructors are running is UB, we still need to keep our own +//! list of destructors. + +use crate::cell::Cell; +use crate::ptr; +use crate::sys::thread_local::destructors; + +pub fn enable() { + #[thread_local] + static REGISTERED: Cell = Cell::new(false); + + extern "C" { + fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); + } + + if !REGISTERED.replace(true) { + // SAFETY: Calling _tlv_atexit while TLS destructors are running is UB. + // But as run_dtors is only called after being registered, this point + // cannot be reached from it. + unsafe { + _tlv_atexit(run_dtors, ptr::null_mut()); + } + } + + unsafe extern "C" fn run_dtors(_: *mut u8) { + unsafe { + destructors::run(); + } + } +} diff --git a/library/std/src/sys/thread_local/guard/key.rs b/library/std/src/sys/thread_local/guard/key.rs new file mode 100644 index 0000000000000..67c3ca8862767 --- /dev/null +++ b/library/std/src/sys/thread_local/guard/key.rs @@ -0,0 +1,23 @@ +//! A lot of UNIX platforms don't have a specialized way to register TLS +//! destructors for native TLS. Instead, we use one TLS key with a destructor +//! that will run all native TLS destructors in the destructor list. + +use crate::ptr; +use crate::sys::thread_local::destructors; +use crate::sys::thread_local::key::{set, LazyKey}; + +pub fn enable() { + static DTORS: LazyKey = LazyKey::new(Some(run)); + + // Setting the key value to something other than NULL will result in the + // destructor being run at thread exit. + unsafe { + set(DTORS.force(), ptr::without_provenance_mut(1)); + } + + unsafe extern "C" fn run(_: *mut u8) { + unsafe { + destructors::run(); + } + } +} diff --git a/library/std/src/sys/thread_local/guard/solid.rs b/library/std/src/sys/thread_local/guard/solid.rs new file mode 100644 index 0000000000000..054b2d561c8b4 --- /dev/null +++ b/library/std/src/sys/thread_local/guard/solid.rs @@ -0,0 +1,24 @@ +//! SOLID, just like macOS, has an API to register TLS destructors. But since +//! it does not allow specifying an argument to that function, and will not run +//! destructors for terminated tasks, we still keep our own list. + +use crate::cell::Cell; +use crate::sys::pal::abi; +use crate::sys::pal::itron::task; +use crate::sys::thread_local::destructors; + +pub fn enable() { + #[thread_local] + static REGISTERED: Cell = Cell::new(false); + + if !REGISTERED.replace(true) { + let tid = task::current_task_id_aborting(); + // Register `tls_dtor` to make sure the TLS destructors are called + // for tasks created by other means than `std::thread` + unsafe { abi::SOLID_TLS_AddDestructor(tid as i32, tls_dtor) }; + } + + unsafe extern "C" fn tls_dtor(_unused: *mut u8) { + unsafe { destructors::run() }; + } +} diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs new file mode 100644 index 0000000000000..bf94f7d6e3d13 --- /dev/null +++ b/library/std/src/sys/thread_local/guard/windows.rs @@ -0,0 +1,92 @@ +//! Support for Windows TLS destructors. +//! +//! Unfortunately, Windows does not provide a nice API to provide a destructor +//! for a TLS variable. Thus, the solution here ended up being a little more +//! obscure, but fear not, the internet has informed me [1][2] that this solution +//! is not unique (no way I could have thought of it as well!). The key idea is +//! to insert some hook somewhere to run arbitrary code on thread termination. +//! With this in place we'll be able to run anything we like, including all +//! TLS destructors! +//! +//! In order to realize this, all TLS destructors are tracked by *us*, not the +//! Windows runtime. This means that we have a global list of destructors for +//! each TLS key or variable that we know about. +//! +//! # What's up with CRT$XLB? +//! +//! For anything about TLS destructors to work on Windows, we have to be able +//! to run *something* when a thread exits. To do so, we place a very special +//! static in a very special location. If this is encoded in just the right +//! way, the kernel's loader is apparently nice enough to run some function +//! of ours whenever a thread exits! How nice of the kernel! +//! +//! Lots of detailed information can be found in source [1] above, but the +//! gist of it is that this is leveraging a feature of Microsoft's PE format +//! (executable format) which is not actually used by any compilers today. +//! This apparently translates to any callbacks in the ".CRT$XLB" section +//! being run on certain events. +//! +//! So after all that, we use the compiler's #[link_section] feature to place +//! a callback pointer into the magic section so it ends up being called. +//! +//! # What's up with this callback? +//! +//! The callback specified receives a number of parameters from... someone! +//! (the kernel? the runtime? I'm not quite sure!) There are a few events that +//! this gets invoked for, but we're currently only interested on when a +//! thread or a process "detaches" (exits). The process part happens for the +//! last thread and the thread part happens for any normal thread. +//! +//! # The article mentions weird stuff about "/INCLUDE"? +//! +//! It sure does! Specifically we're talking about this quote: +//! +//! ```quote +//! The Microsoft run-time library facilitates this process by defining a +//! memory image of the TLS Directory and giving it the special name +//! “__tls_used” (Intel x86 platforms) or “_tls_used” (other platforms). The +//! linker looks for this memory image and uses the data there to create the +//! TLS Directory. Other compilers that support TLS and work with the +//! Microsoft linker must use this same technique. +//! ``` +//! +//! Basically what this means is that if we want support for our TLS +//! destructors/our hook being called then we need to make sure the linker does +//! not omit this symbol. Otherwise it will omit it and our callback won't be +//! wired up. +//! +//! We don't actually use the `/INCLUDE` linker flag here like the article +//! mentions because the Rust compiler doesn't propagate linker flags, but +//! instead we use a shim function which performs a volatile 1-byte load from +//! the address of the symbol to ensure it sticks around. +//! +//! [1]: https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way +//! [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base/threading/thread_local_storage_win.cc#L42 + +use core::ffi::c_void; + +use crate::ptr; +use crate::sys::c; + +pub fn enable() { + // When destructors are used, we don't want LLVM eliminating CALLBACK for any + // reason. Once the symbol makes it to the linker, it will do the rest. + unsafe { ptr::from_ref(&CALLBACK).read_volatile() }; +} + +#[link_section = ".CRT$XLB"] +#[cfg_attr(miri, used)] // Miri only considers explicitly `#[used]` statics for `lookup_link_section` +pub static CALLBACK: unsafe extern "system" fn(*mut c_void, u32, *mut c_void) = tls_callback; + +unsafe extern "system" fn tls_callback(_h: *mut c_void, dw_reason: u32, _pv: *mut c_void) { + if dw_reason == c::DLL_THREAD_DETACH || dw_reason == c::DLL_PROCESS_DETACH { + #[cfg(target_thread_local)] + unsafe { + super::super::destructors::run(); + } + #[cfg(not(target_thread_local))] + unsafe { + super::super::key::run_dtors(); + } + } +} diff --git a/library/std/src/sys/thread_local/key/racy.rs b/library/std/src/sys/thread_local/key/racy.rs new file mode 100644 index 0000000000000..69f11458c3289 --- /dev/null +++ b/library/std/src/sys/thread_local/key/racy.rs @@ -0,0 +1,82 @@ +//! A `LazyKey` implementation using racy initialization. +//! +//! Unfortunately, none of the platforms currently supported by `std` allows +//! creating TLS keys at compile-time. Thus we need a way to lazily create keys. +//! Instead of blocking API like `OnceLock`, we use racy initialization, which +//! should be more lightweight and avoids circular dependencies with the rest of +//! `std`. + +use crate::sync::atomic::{self, AtomicUsize, Ordering}; + +/// A type for TLS keys that are statically allocated. +/// +/// This is basically a `LazyLock`, but avoids blocking and circular +/// dependencies with the rest of `std`. +pub struct LazyKey { + /// Inner static TLS key (internals). + key: AtomicUsize, + /// Destructor for the TLS value. + dtor: Option, +} + +// Define a sentinel value that is likely not to be returned +// as a TLS key. +#[cfg(not(target_os = "nto"))] +const KEY_SENTVAL: usize = 0; +// On QNX Neutrino, 0 is always returned when currently not in use. +// Using 0 would mean to always create two keys and remote the first +// one (with value of 0) immediately afterwards. +#[cfg(target_os = "nto")] +const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1; + +impl LazyKey { + #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] + pub const fn new(dtor: Option) -> LazyKey { + LazyKey { key: atomic::AtomicUsize::new(KEY_SENTVAL), dtor } + } + + #[inline] + pub fn force(&self) -> super::Key { + match self.key.load(Ordering::Acquire) { + KEY_SENTVAL => self.lazy_init() as super::Key, + n => n as super::Key, + } + } + + fn lazy_init(&self) -> usize { + // POSIX allows the key created here to be KEY_SENTVAL, but the compare_exchange + // below relies on using KEY_SENTVAL as a sentinel value to check who won the + // race to set the shared TLS key. As far as I know, there is no + // guaranteed value that cannot be returned as a posix_key_create key, + // so there is no value we can initialize the inner key with to + // prove that it has not yet been set. As such, we'll continue using a + // value of KEY_SENTVAL, but with some gyrations to make sure we have a non-KEY_SENTVAL + // value returned from the creation routine. + // FIXME: this is clearly a hack, and should be cleaned up. + let key1 = super::create(self.dtor); + let key = if key1 as usize != KEY_SENTVAL { + key1 + } else { + let key2 = super::create(self.dtor); + unsafe { + super::destroy(key1); + } + key2 + }; + rtassert!(key as usize != KEY_SENTVAL); + match self.key.compare_exchange( + KEY_SENTVAL, + key as usize, + Ordering::Release, + Ordering::Acquire, + ) { + // The CAS succeeded, so we've created the actual key + Ok(_) => key as usize, + // If someone beat us to the punch, use their key instead + Err(n) => unsafe { + super::destroy(key); + n + }, + } + } +} diff --git a/library/std/src/sys/pal/sgx/thread_local_key.rs b/library/std/src/sys/thread_local/key/sgx.rs similarity index 74% rename from library/std/src/sys/pal/sgx/thread_local_key.rs rename to library/std/src/sys/thread_local/key/sgx.rs index c7a57d3a3d47e..4aa2e5afa72ef 100644 --- a/library/std/src/sys/pal/sgx/thread_local_key.rs +++ b/library/std/src/sys/thread_local/key/sgx.rs @@ -1,9 +1,9 @@ -use super::abi::tls::{Key as AbiKey, Tls}; +use crate::sys::pal::abi::tls::{Key as AbiKey, Tls}; pub type Key = usize; #[inline] -pub unsafe fn create(dtor: Option) -> Key { +pub fn create(dtor: Option) -> Key { Tls::create(dtor).as_usize() } diff --git a/library/std/src/sys/thread_local/key/tests.rs b/library/std/src/sys/thread_local/key/tests.rs new file mode 100644 index 0000000000000..d82b34e71f0e4 --- /dev/null +++ b/library/std/src/sys/thread_local/key/tests.rs @@ -0,0 +1,62 @@ +use super::{get, set, LazyKey}; +use crate::ptr; + +#[test] +fn smoke() { + static K1: LazyKey = LazyKey::new(None); + static K2: LazyKey = LazyKey::new(None); + + let k1 = K1.force(); + let k2 = K2.force(); + assert_ne!(k1, k2); + + assert_eq!(K1.force(), k1); + assert_eq!(K2.force(), k2); + + unsafe { + assert!(get(k1).is_null()); + assert!(get(k2).is_null()); + set(k1, ptr::without_provenance_mut(1)); + set(k2, ptr::without_provenance_mut(2)); + assert_eq!(get(k1) as usize, 1); + assert_eq!(get(k2) as usize, 2); + } +} + +#[test] +fn destructors() { + use crate::mem::ManuallyDrop; + use crate::sync::Arc; + use crate::thread; + + unsafe extern "C" fn destruct(ptr: *mut u8) { + drop(unsafe { Arc::from_raw(ptr as *const ()) }); + } + + static KEY: LazyKey = LazyKey::new(Some(destruct)); + + let shared1 = Arc::new(()); + let shared2 = Arc::clone(&shared1); + + let key = KEY.force(); + unsafe { + assert!(get(key).is_null()); + set(key, Arc::into_raw(shared1) as *mut u8); + } + + thread::spawn(move || unsafe { + let key = KEY.force(); + assert!(get(key).is_null()); + set(key, Arc::into_raw(shared2) as *mut u8); + }) + .join() + .unwrap(); + + // Leak the Arc, let the TLS destructor clean it up. + let shared1 = unsafe { ManuallyDrop::new(Arc::from_raw(get(key) as *const ())) }; + assert_eq!( + Arc::strong_count(&shared1), + 1, + "destructor should have dropped the other reference on thread exit" + ); +} diff --git a/library/std/src/sys/thread_local/key/unix.rs b/library/std/src/sys/thread_local/key/unix.rs new file mode 100644 index 0000000000000..28e48a750b9bf --- /dev/null +++ b/library/std/src/sys/thread_local/key/unix.rs @@ -0,0 +1,28 @@ +use crate::mem; + +pub type Key = libc::pthread_key_t; + +#[inline] +pub fn create(dtor: Option) -> Key { + let mut key = 0; + assert_eq!(unsafe { libc::pthread_key_create(&mut key, mem::transmute(dtor)) }, 0); + key +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let r = unsafe { libc::pthread_setspecific(key, value as *mut _) }; + debug_assert_eq!(r, 0); +} + +#[inline] +#[cfg(any(not(target_thread_local), test))] +pub unsafe fn get(key: Key) -> *mut u8 { + unsafe { libc::pthread_getspecific(key) as *mut u8 } +} + +#[inline] +pub unsafe fn destroy(key: Key) { + let r = unsafe { libc::pthread_key_delete(key) }; + debug_assert_eq!(r, 0); +} diff --git a/library/std/src/sys/thread_local/key/windows.rs b/library/std/src/sys/thread_local/key/windows.rs new file mode 100644 index 0000000000000..f4e0f25a476ee --- /dev/null +++ b/library/std/src/sys/thread_local/key/windows.rs @@ -0,0 +1,204 @@ +//! Implementation of `LazyKey` for Windows. +//! +//! Windows has no native support for running destructors so we manage our own +//! list of destructors to keep track of how to destroy keys. We then install a +//! callback later to get invoked whenever a thread exits, running all +//! appropriate destructors (see the [`guard`](guard) module documentation). +//! +//! This will likely need to be improved over time, but this module attempts a +//! "poor man's" destructor callback system. Once we've got a list of what to +//! run, we iterate over all keys, check their values, and then run destructors +//! if the values turn out to be non null (setting them to null just beforehand). +//! We do this a few times in a loop to basically match Unix semantics. If we +//! don't reach a fixed point after a short while then we just inevitably leak +//! something. +//! +//! The list is implemented as an atomic single-linked list of `LazyKey`s and +//! does not support unregistration. Unfortunately, this means that we cannot +//! use racy initialization for creating the keys in `LazyKey`, as that could +//! result in destructors being missed. Hence, we synchronize the creation of +//! keys with destructors through [`INIT_ONCE`](c::INIT_ONCE) (`std`'s +//! [`Once`](crate::sync::Once) cannot be used since it might use TLS itself). +//! For keys without destructors, racy initialization suffices. + +// FIXME: investigate using a fixed-size array instead, as the maximum number +// of keys is [limited to 1088](https://learn.microsoft.com/en-us/windows/win32/ProcThread/thread-local-storage). + +use crate::cell::UnsafeCell; +use crate::ptr; +use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release}; +use crate::sync::atomic::{AtomicPtr, AtomicU32}; +use crate::sys::c; +use crate::sys::thread_local::guard; + +pub type Key = u32; +type Dtor = unsafe extern "C" fn(*mut u8); + +pub struct LazyKey { + /// The key value shifted up by one. Since TLS_OUT_OF_INDEXES == u32::MAX + /// is not a valid key value, this allows us to use zero as sentinel value + /// without risking overflow. + key: AtomicU32, + dtor: Option, + next: AtomicPtr, + /// Currently, destructors cannot be unregistered, so we cannot use racy + /// initialization for keys. Instead, we need synchronize initialization. + /// Use the Windows-provided `Once` since it does not require TLS. + once: UnsafeCell, +} + +impl LazyKey { + #[inline] + pub const fn new(dtor: Option) -> LazyKey { + LazyKey { + key: AtomicU32::new(0), + dtor, + next: AtomicPtr::new(ptr::null_mut()), + once: UnsafeCell::new(c::INIT_ONCE_STATIC_INIT), + } + } + + #[inline] + pub fn force(&'static self) -> Key { + match self.key.load(Acquire) { + 0 => unsafe { self.init() }, + key => key - 1, + } + } + + #[cold] + unsafe fn init(&'static self) -> Key { + if self.dtor.is_some() { + let mut pending = c::FALSE; + let r = unsafe { + c::InitOnceBeginInitialize(self.once.get(), 0, &mut pending, ptr::null_mut()) + }; + assert_eq!(r, c::TRUE); + + if pending == c::FALSE { + // Some other thread initialized the key, load it. + self.key.load(Relaxed) - 1 + } else { + let key = unsafe { c::TlsAlloc() }; + if key == c::TLS_OUT_OF_INDEXES { + // Wakeup the waiting threads before panicking to avoid deadlock. + unsafe { + c::InitOnceComplete( + self.once.get(), + c::INIT_ONCE_INIT_FAILED, + ptr::null_mut(), + ); + } + panic!("out of TLS indexes"); + } + + unsafe { + register_dtor(self); + } + + // Release-storing the key needs to be the last thing we do. + // This is because in `fn key()`, other threads will do an acquire load of the key, + // and if that sees this write then it will entirely bypass the `InitOnce`. We thus + // need to establish synchronization through `key`. In particular that acquire load + // must happen-after the register_dtor above, to ensure the dtor actually runs! + self.key.store(key + 1, Release); + + let r = unsafe { c::InitOnceComplete(self.once.get(), 0, ptr::null_mut()) }; + debug_assert_eq!(r, c::TRUE); + + key + } + } else { + // If there is no destructor to clean up, we can use racy initialization. + + let key = unsafe { c::TlsAlloc() }; + assert_ne!(key, c::TLS_OUT_OF_INDEXES, "out of TLS indexes"); + + match self.key.compare_exchange(0, key + 1, AcqRel, Acquire) { + Ok(_) => key, + Err(new) => unsafe { + // Some other thread completed initialization first, so destroy + // our key and use theirs. + let r = c::TlsFree(key); + debug_assert_eq!(r, c::TRUE); + new - 1 + }, + } + } + } +} + +unsafe impl Send for LazyKey {} +unsafe impl Sync for LazyKey {} + +#[inline] +pub unsafe fn set(key: Key, val: *mut u8) { + let r = unsafe { c::TlsSetValue(key, val.cast()) }; + debug_assert_eq!(r, c::TRUE); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + unsafe { c::TlsGetValue(key).cast() } +} + +static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + +/// Should only be called once per key, otherwise loops or breaks may occur in +/// the linked list. +unsafe fn register_dtor(key: &'static LazyKey) { + guard::enable(); + + let this = <*const LazyKey>::cast_mut(key); + // Use acquire ordering to pass along the changes done by the previously + // registered keys when we store the new head with release ordering. + let mut head = DTORS.load(Acquire); + loop { + key.next.store(head, Relaxed); + match DTORS.compare_exchange_weak(head, this, Release, Acquire) { + Ok(_) => break, + Err(new) => head = new, + } + } +} + +/// This will and must only be run by the destructor callback in [`guard`]. +pub unsafe fn run_dtors() { + for _ in 0..5 { + let mut any_run = false; + + // Use acquire ordering to observe key initialization. + let mut cur = DTORS.load(Acquire); + while !cur.is_null() { + let pre_key = unsafe { (*cur).key.load(Acquire) }; + let dtor = unsafe { (*cur).dtor.unwrap() }; + cur = unsafe { (*cur).next.load(Relaxed) }; + + // In LazyKey::init, we register the dtor before setting `key`. + // So if one thread's `run_dtors` races with another thread executing `init` on the same + // `LazyKey`, we can encounter a key of 0 here. That means this key was never + // initialized in this thread so we can safely skip it. + if pre_key == 0 { + continue; + } + // If this is non-zero, then via the `Acquire` load above we synchronized with + // everything relevant for this key. (It's not clear that this is needed, since the + // release-acquire pair on DTORS also establishes synchronization, but better safe than + // sorry.) + let key = pre_key - 1; + + let ptr = unsafe { c::TlsGetValue(key) }; + if !ptr.is_null() { + unsafe { + c::TlsSetValue(key, ptr::null_mut()); + dtor(ptr as *mut _); + any_run = true; + } + } + } + + if !any_run { + break; + } + } +} diff --git a/library/std/src/sys/pal/xous/thread_local_key.rs b/library/std/src/sys/thread_local/key/xous.rs similarity index 71% rename from library/std/src/sys/pal/xous/thread_local_key.rs rename to library/std/src/sys/thread_local/key/xous.rs index 6c29813c79dfd..4fb2fdcc61925 100644 --- a/library/std/src/sys/pal/xous/thread_local_key.rs +++ b/library/std/src/sys/thread_local/key/xous.rs @@ -1,24 +1,50 @@ -use crate::mem::ManuallyDrop; -use crate::ptr; -use crate::sync::atomic::AtomicPtr; -use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +//! Thread Local Storage +//! +//! Currently, we are limited to 1023 TLS entries. The entries +//! live in a page of memory that's unique per-process, and is +//! stored in the `$tp` register. If this register is 0, then +//! TLS has not been initialized and thread cleanup can be skipped. +//! +//! The index into this register is the `key`. This key is identical +//! between all threads, but indexes a different offset within this +//! pointer. +//! +//! # Dtor registration (stolen from Windows) +//! +//! Xous has no native support for running destructors so we manage our own +//! list of destructors to keep track of how to destroy keys. When a thread +//! or the process exits, `run_dtors` is called, which will iterate through +//! the list and run the destructors. +//! +//! Currently unregistration from this list is not supported. A destructor can be +//! registered but cannot be unregistered. There's various simplifying reasons +//! for doing this, the big ones being: +//! +//! 1. Currently we don't even support deallocating TLS keys, so normal operation +//! doesn't need to deallocate a destructor. +//! 2. There is no point in time where we know we can unregister a destructor +//! because it could always be getting run by some remote thread. +//! +//! Typically processes have a statically known set of TLS keys which is pretty +//! small, and we'd want to keep this memory alive for the whole process anyway +//! really. +//! +//! Perhaps one day we can fold the `Box` here into a static allocation, +//! expanding the `LazyKey` structure to contain not only a slot for the TLS +//! key but also a slot for the destructor queue on windows. An optimization for +//! another day! + +// FIXME(joboet): implement support for native TLS instead. + use core::arch::asm; +use crate::mem::ManuallyDrop; use crate::os::xous::ffi::{map_memory, unmap_memory, MemoryFlags}; +use crate::ptr; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sync::atomic::{AtomicPtr, AtomicUsize}; -/// Thread Local Storage -/// -/// Currently, we are limited to 1023 TLS entries. The entries -/// live in a page of memory that's unique per-process, and is -/// stored in the `$tp` register. If this register is 0, then -/// TLS has not been initialized and thread cleanup can be skipped. -/// -/// The index into this register is the `key`. This key is identical -/// between all threads, but indexes a different offset within this -/// pointer. pub type Key = usize; - pub type Dtor = unsafe extern "C" fn(*mut u8); const TLS_MEMORY_SIZE: usize = 4096; @@ -52,7 +78,7 @@ fn tls_ptr_addr() -> *mut *mut u8 { core::ptr::with_exposed_provenance_mut::<*mut u8>(tp) } -/// Create an area of memory that's unique per thread. This area will +/// Creates an area of memory that's unique per thread. This area will /// contain all thread local pointers. fn tls_table() -> &'static mut [*mut u8] { let tp = tls_ptr_addr(); @@ -89,7 +115,7 @@ fn tls_table() -> &'static mut [*mut u8] { } #[inline] -pub unsafe fn create(dtor: Option) -> Key { +pub fn create(dtor: Option) -> Key { // Allocate a new TLS key. These keys are shared among all threads. #[allow(unused_unsafe)] let key = unsafe { TLS_KEY_INDEX.fetch_add(1, Relaxed) }; @@ -118,32 +144,6 @@ pub unsafe fn destroy(_key: Key) { // lots of TLS variables, but in practice that's not an issue. } -// ------------------------------------------------------------------------- -// Dtor registration (stolen from Windows) -// -// Xous has no native support for running destructors so we manage our own -// list of destructors to keep track of how to destroy keys. We then install a -// callback later to get invoked whenever a thread exits, running all -// appropriate destructors. -// -// Currently unregistration from this list is not supported. A destructor can be -// registered but cannot be unregistered. There's various simplifying reasons -// for doing this, the big ones being: -// -// 1. Currently we don't even support deallocating TLS keys, so normal operation -// doesn't need to deallocate a destructor. -// 2. There is no point in time where we know we can unregister a destructor -// because it could always be getting run by some remote thread. -// -// Typically processes have a statically known set of TLS keys which is pretty -// small, and we'd want to keep this memory alive for the whole process anyway -// really. -// -// Perhaps one day we can fold the `Box` here into a static allocation, -// expanding the `StaticKey` structure to contain not only a slot for the TLS -// key but also a slot for the destructor queue on windows. An optimization for -// another day! - struct Node { dtor: Dtor, key: Key, diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index 0a78a1a1cf02d..3d1b91a7ea095 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -1,27 +1,159 @@ -#![unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")] -#![cfg_attr(test, allow(unused))] +//! Implementation of the `thread_local` macro. +//! +//! There are three different thread-local implementations: +//! * Some targets lack threading support, and hence have only one thread, so +//! the TLS data is stored in a normal `static`. +//! * Some targets support TLS natively via the dynamic linker and C runtime. +//! * On some targets, the OS provides a library-based TLS implementation. The +//! TLS data is heap-allocated and referenced using a TLS key. +//! +//! Each implementation provides a macro which generates the `LocalKey` `const` +//! used to reference the TLS variable, along with the necessary helper structs +//! to track the initialization/destruction state of the variable. +//! +//! Additionally, this module contains abstractions for the OS interfaces used +//! for these implementations. -// There are three thread-local implementations: "static", "fast", "OS". -// The "OS" thread local key type is accessed via platform-specific API calls and is slow, while the -// "fast" key type is accessed via code generated via LLVM, where TLS keys are set up by the linker. -// "static" is for single-threaded platforms where a global static is sufficient. +#![cfg_attr(test, allow(unused))] +#![doc(hidden)] +#![forbid(unsafe_op_in_unsafe_fn)] +#![unstable( + feature = "thread_local_internals", + reason = "internal details of the thread_local macro", + issue = "none" +)] cfg_if::cfg_if! { - if #[cfg(any(all(target_family = "wasm", not(target_feature = "atomics")), target_os = "uefi"))] { - #[doc(hidden)] - mod static_local; - #[doc(hidden)] - pub use static_local::{EagerStorage, LazyStorage, thread_local_inner}; + if #[cfg(any( + all(target_family = "wasm", not(target_feature = "atomics")), + target_os = "uefi", + target_os = "zkvm", + ))] { + mod statik; + pub use statik::{EagerStorage, LazyStorage, thread_local_inner}; } else if #[cfg(target_thread_local)] { - #[doc(hidden)] - mod fast_local; - #[doc(hidden)] - pub use fast_local::{EagerStorage, LazyStorage, thread_local_inner}; + mod native; + pub use native::{EagerStorage, LazyStorage, thread_local_inner}; } else { - #[doc(hidden)] - mod os_local; - #[doc(hidden)] - pub use os_local::{Key, thread_local_inner}; + mod os; + pub use os::{Storage, thread_local_inner}; + } +} + +/// The native TLS implementation needs a way to register destructors for its data. +/// This module contains platform-specific implementations of that register. +/// +/// It turns out however that most platforms don't have a way to register a +/// destructor for each variable. On these platforms, we keep track of the +/// destructors ourselves and register (through the [`guard`] module) only a +/// single callback that runs all of the destructors in the list. +#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))] +pub(crate) mod destructors { + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "fuchsia", + target_os = "redox", + target_os = "hurd", + target_os = "netbsd", + target_os = "dragonfly" + ))] { + mod linux_like; + mod list; + pub(super) use linux_like::register; + pub(super) use list::run; + } else { + mod list; + pub(super) use list::register; + pub(crate) use list::run; + } + } +} + +/// This module provides a way to schedule the execution of the destructor list +/// on systems without a per-variable destructor system. +mod guard { + cfg_if::cfg_if! { + if #[cfg(all(target_thread_local, target_vendor = "apple"))] { + mod apple; + pub(super) use apple::enable; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub(super) use windows::enable; + } else if #[cfg(any( + all(target_family = "wasm", target_feature = "atomics"), + ))] { + pub(super) fn enable() { + // FIXME: Right now there is no concept of "thread exit", but + // this is likely going to show up at some point in the form of + // an exported symbol that the wasm runtime is going to be + // expected to call. For now we just leak everything, but if + // such a function starts to exist it will probably need to + // iterate the destructor list with this function: + #[allow(unused)] + use super::destructors::run; + } + } else if #[cfg(target_os = "hermit")] { + pub(super) fn enable() {} + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub(super) use solid::enable; + } else if #[cfg(all(target_thread_local, not(target_family = "wasm")))] { + mod key; + pub(super) use key::enable; + } + } +} + +/// `const`-creatable TLS keys. +/// +/// Most OSs without native TLS will provide a library-based way to create TLS +/// storage. For each TLS variable, we create a key, which can then be used to +/// reference an entry in a thread-local table. This then associates each key +/// with a pointer which we can get and set to store our data. +pub(crate) mod key { + cfg_if::cfg_if! { + if #[cfg(any( + all( + not(target_vendor = "apple"), + not(target_family = "wasm"), + target_family = "unix", + ), + target_os = "teeos", + ))] { + mod racy; + mod unix; + #[cfg(test)] + mod tests; + pub(super) use racy::LazyKey; + pub(super) use unix::{Key, set}; + #[cfg(any(not(target_thread_local), test))] + pub(super) use unix::get; + use unix::{create, destroy}; + } else if #[cfg(all(not(target_thread_local), target_os = "windows"))] { + #[cfg(test)] + mod tests; + mod windows; + pub(super) use windows::{Key, LazyKey, get, run_dtors, set}; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod racy; + mod sgx; + #[cfg(test)] + mod tests; + pub(super) use racy::LazyKey; + pub(super) use sgx::{Key, get, set}; + use sgx::{create, destroy}; + } else if #[cfg(target_os = "xous")] { + mod racy; + #[cfg(test)] + mod tests; + mod xous; + pub(super) use racy::LazyKey; + pub(crate) use xous::destroy_tls; + pub(super) use xous::{Key, get, set}; + use xous::{create, destroy}; + } } } diff --git a/library/std/src/sys/thread_local/fast_local/eager.rs b/library/std/src/sys/thread_local/native/eager.rs similarity index 51% rename from library/std/src/sys/thread_local/fast_local/eager.rs rename to library/std/src/sys/thread_local/native/eager.rs index c2bc580530ba4..fd48c4f720216 100644 --- a/library/std/src/sys/thread_local/fast_local/eager.rs +++ b/library/std/src/sys/thread_local/native/eager.rs @@ -1,7 +1,6 @@ use crate::cell::{Cell, UnsafeCell}; use crate::ptr::{self, drop_in_place}; -use crate::sys::thread_local::abort_on_dtor_unwind; -use crate::sys::thread_local_dtor::register_dtor; +use crate::sys::thread_local::{abort_on_dtor_unwind, destructors}; #[derive(Clone, Copy)] enum State { @@ -21,43 +20,35 @@ impl Storage { Storage { state: Cell::new(State::Initial), val: UnsafeCell::new(val) } } - /// Get a reference to the TLS value. If the TLS variable has been destroyed, - /// `None` is returned. + /// Gets a pointer to the TLS value. If the TLS variable has been destroyed, + /// a null pointer is returned. /// - /// # Safety - /// * The `self` reference must remain valid until the TLS destructor has been - /// run. - /// * The returned reference may only be used until thread destruction occurs - /// and may not be used after reentrant initialization has occurred. + /// The resulting pointer may not be used after thread destruction has + /// occurred. /// - // FIXME(#110897): return NonNull instead of lying about the lifetime. + /// # Safety + /// The `self` reference must remain valid until the TLS destructor is run. #[inline] - pub unsafe fn get(&self) -> Option<&'static T> { + pub unsafe fn get(&self) -> *const T { match self.state.get() { - // SAFETY: as the state is not `Destroyed`, the value cannot have - // been destroyed yet. The reference fulfills the terms outlined - // above. - State::Alive => unsafe { Some(&*self.val.get()) }, - State::Destroyed => None, + State::Alive => self.val.get(), + State::Destroyed => ptr::null(), State::Initial => unsafe { self.initialize() }, } } #[cold] - unsafe fn initialize(&self) -> Option<&'static T> { + unsafe fn initialize(&self) -> *const T { // Register the destructor // SAFETY: - // * the destructor will be called at thread destruction. - // * the caller guarantees that `self` will be valid until that time. + // The caller guarantees that `self` will be valid until thread destruction. unsafe { - register_dtor(ptr::from_ref(self).cast_mut().cast(), destroy::); + destructors::register(ptr::from_ref(self).cast_mut().cast(), destroy::); } + self.state.set(State::Alive); - // SAFETY: as the state is not `Destroyed`, the value cannot have - // been destroyed yet. The reference fulfills the terms outlined - // above. - unsafe { Some(&*self.val.get()) } + self.val.get() } } diff --git a/library/std/src/sys/thread_local/fast_local/lazy.rs b/library/std/src/sys/thread_local/native/lazy.rs similarity index 59% rename from library/std/src/sys/thread_local/fast_local/lazy.rs rename to library/std/src/sys/thread_local/native/lazy.rs index c2e9a17145468..51294285ba013 100644 --- a/library/std/src/sys/thread_local/fast_local/lazy.rs +++ b/library/std/src/sys/thread_local/native/lazy.rs @@ -1,8 +1,7 @@ use crate::cell::UnsafeCell; use crate::hint::unreachable_unchecked; use crate::ptr; -use crate::sys::thread_local::abort_on_dtor_unwind; -use crate::sys::thread_local_dtor::register_dtor; +use crate::sys::thread_local::{abort_on_dtor_unwind, destructors}; pub unsafe trait DestroyedState: Sized { fn register_dtor(s: &Storage); @@ -15,7 +14,7 @@ unsafe impl DestroyedState for ! { unsafe impl DestroyedState for () { fn register_dtor(s: &Storage) { unsafe { - register_dtor(ptr::from_ref(s).cast_mut().cast(), destroy::); + destructors::register(ptr::from_ref(s).cast_mut().cast(), destroy::); } } } @@ -39,49 +38,31 @@ where Storage { state: UnsafeCell::new(State::Initial) } } - /// Get a reference to the TLS value, potentially initializing it with the - /// provided parameters. If the TLS variable has been destroyed, `None` is - /// returned. + /// Gets a pointer to the TLS value, potentially initializing it with the + /// provided parameters. If the TLS variable has been destroyed, a null + /// pointer is returned. /// - /// # Safety - /// * The `self` reference must remain valid until the TLS destructor is run, - /// at which point the returned reference is invalidated. - /// * The returned reference may only be used until thread destruction occurs - /// and may not be used after reentrant initialization has occurred. + /// The resulting pointer may not be used after reentrant inialialization + /// or thread destruction has occurred. /// - // FIXME(#110897): return NonNull instead of lying about the lifetime. + /// # Safety + /// The `self` reference must remain valid until the TLS destructor is run. #[inline] - pub unsafe fn get_or_init( - &self, - i: Option<&mut Option>, - f: impl FnOnce() -> T, - ) -> Option<&'static T> { - // SAFETY: - // No mutable reference to the inner value exists outside the calls to - // `replace`. The lifetime of the returned reference fulfills the terms - // outlined above. + pub unsafe fn get_or_init(&self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { let state = unsafe { &*self.state.get() }; match state { - State::Alive(v) => Some(v), - State::Destroyed(_) => None, + State::Alive(v) => v, + State::Destroyed(_) => ptr::null(), State::Initial => unsafe { self.initialize(i, f) }, } } #[cold] - unsafe fn initialize( - &self, - i: Option<&mut Option>, - f: impl FnOnce() -> T, - ) -> Option<&'static T> { + unsafe fn initialize(&self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { // Perform initialization let v = i.and_then(Option::take).unwrap_or_else(f); - // SAFETY: - // If references to the inner value exist, they were created in `f` - // and are invalidated here. The caller promises to never use them - // after this. let old = unsafe { self.state.get().replace(State::Alive(v)) }; match old { // If the variable is not being recursively initialized, register @@ -92,12 +73,10 @@ where val => drop(val), } - // SAFETY: - // Initialization was completed and the state was set to `Alive`, so the - // reference fulfills the terms outlined above. + // SAFETY: the state was just set to `Alive` unsafe { let State::Alive(v) = &*self.state.get() else { unreachable_unchecked() }; - Some(v) + v } } } diff --git a/library/std/src/sys/thread_local/fast_local/mod.rs b/library/std/src/sys/thread_local/native/mod.rs similarity index 64% rename from library/std/src/sys/thread_local/fast_local/mod.rs rename to library/std/src/sys/thread_local/native/mod.rs index 25379071cb7a6..1cc45fe892dee 100644 --- a/library/std/src/sys/thread_local/fast_local/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -1,7 +1,7 @@ //! Thread local support for platforms with native TLS. //! //! To achieve the best performance, we choose from four different types for -//! the TLS variable, depending from the method of initialization used (`const` +//! the TLS variable, depending on the method of initialization used (`const` //! or lazy) and the drop requirements of the stored type: //! //! | | `Drop` | `!Drop` | @@ -29,8 +29,6 @@ //! eliminates the `Destroyed` state for these values, which can allow more niche //! optimizations to occur for the `State` enum. For `Drop` types, `()` is used. -#![deny(unsafe_op_in_unsafe_fn)] - mod eager; mod lazy; @@ -52,32 +50,26 @@ pub macro thread_local_inner { (@key $t:ty, const $init:expr) => {{ const __INIT: $t = $init; - #[inline] - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn __getit( - _init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - use $crate::thread::local_impl::EagerStorage; + unsafe { use $crate::mem::needs_drop; - use $crate::ptr::addr_of; + use $crate::thread::LocalKey; + use $crate::thread::local_impl::EagerStorage; - if needs_drop::<$t>() { - #[thread_local] - static VAL: EagerStorage<$t> = EagerStorage::new(__INIT); - unsafe { - VAL.get() + LocalKey::new(const { + if needs_drop::<$t>() { + |_| { + #[thread_local] + static VAL: EagerStorage<$t> = EagerStorage::new(__INIT); + VAL.get() + } + } else { + |_| { + #[thread_local] + static VAL: $t = __INIT; + &VAL + } } - } else { - #[thread_local] - static VAL: $t = __INIT; - unsafe { - $crate::option::Option::Some(&*addr_of!(VAL)) - } - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) + }) } }}, @@ -88,31 +80,26 @@ pub macro thread_local_inner { $init } - #[inline] - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn __getit( - init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - use $crate::thread::local_impl::LazyStorage; + unsafe { use $crate::mem::needs_drop; + use $crate::thread::LocalKey; + use $crate::thread::local_impl::LazyStorage; - if needs_drop::<$t>() { - #[thread_local] - static VAL: LazyStorage<$t, ()> = LazyStorage::new(); - unsafe { - VAL.get_or_init(init, __init) + LocalKey::new(const { + if needs_drop::<$t>() { + |init| { + #[thread_local] + static VAL: LazyStorage<$t, ()> = LazyStorage::new(); + VAL.get_or_init(init, __init) + } + } else { + |init| { + #[thread_local] + static VAL: LazyStorage<$t, !> = LazyStorage::new(); + VAL.get_or_init(init, __init) + } } - } else { - #[thread_local] - static VAL: LazyStorage<$t, !> = LazyStorage::new(); - unsafe { - VAL.get_or_init(init, __init) - } - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) + }) } }}, ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { diff --git a/library/std/src/sys/thread_local/os_local.rs b/library/std/src/sys/thread_local/os.rs similarity index 52% rename from library/std/src/sys/thread_local/os_local.rs rename to library/std/src/sys/thread_local/os.rs index d6ddbb78a9c86..e27b47c3f4536 100644 --- a/library/std/src/sys/thread_local/os_local.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -2,7 +2,7 @@ use super::abort_on_dtor_unwind; use crate::cell::Cell; use crate::marker::PhantomData; use crate::ptr; -use crate::sys_common::thread_local_key::StaticKey as OsKey; +use crate::sys::thread_local::key::{get, set, Key, LazyKey}; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] @@ -16,30 +16,22 @@ pub macro thread_local_inner { }, // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => { - { - #[inline] - fn __init() -> $t { $init } + (@key $t:ty, $init:expr) => {{ + #[inline] + fn __init() -> $t { $init } - // `#[inline] does not work on windows-gnu due to linking errors around dllimports. - // See https://github.com/rust-lang/rust/issues/109797. - #[cfg_attr(not(windows), inline)] - unsafe fn __getit( - init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - use $crate::thread::local_impl::Key; - - static __KEY: Key<$t> = Key::new(); - unsafe { - __KEY.get(init, __init) - } - } + unsafe { + use $crate::thread::LocalKey; + use $crate::thread::local_impl::Storage; - unsafe { - $crate::thread::LocalKey::new(__getit) - } + // Inlining does not work on windows-gnu due to linking errors around + // dllimports. See https://github.com/rust-lang/rust/issues/109797. + LocalKey::new(#[cfg_attr(windows, inline(never))] |init| { + static VAL: Storage<$t> = Storage::new(); + VAL.get(init, __init) + }) } - }, + }}, ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); @@ -49,66 +41,71 @@ pub macro thread_local_inner { /// Use a regular global static to store this key; the state provided will then be /// thread-local. #[allow(missing_debug_implementations)] -pub struct Key { - os: OsKey, +pub struct Storage { + key: LazyKey, marker: PhantomData>, } -unsafe impl Sync for Key {} +unsafe impl Sync for Storage {} struct Value { value: T, - key: &'static Key, + // INVARIANT: if this value is stored under a TLS key, `key` must be that `key`. + key: Key, } -impl Key { +impl Storage { #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] - pub const fn new() -> Key { - Key { os: OsKey::new(Some(destroy_value::)), marker: PhantomData } + pub const fn new() -> Storage { + Storage { key: LazyKey::new(Some(destroy_value::)), marker: PhantomData } } - /// Get the value associated with this key, initializating it if necessary. + /// Gets a pointer to the TLS value, potentially initializing it with the + /// provided parameters. If the TLS variable has been destroyed, a null + /// pointer is returned. /// - /// # Safety - /// * the returned reference must not be used after recursive initialization - /// or thread destruction occurs. - pub unsafe fn get( - &'static self, - i: Option<&mut Option>, - f: impl FnOnce() -> T, - ) -> Option<&'static T> { - // SAFETY: (FIXME: get should actually be safe) - let ptr = unsafe { self.os.get() as *mut Value }; + /// The resulting pointer may not be used after reentrant inialialization + /// or thread destruction has occurred. + pub fn get(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { + let key = self.key.force(); + let ptr = unsafe { get(key) as *mut Value }; if ptr.addr() > 1 { // SAFETY: the check ensured the pointer is safe (its destructor // is not running) + it is coming from a trusted source (self). - unsafe { Some(&(*ptr).value) } + unsafe { &(*ptr).value } } else { - // SAFETY: At this point we are sure we have no value and so - // initializing (or trying to) is safe. - unsafe { self.try_initialize(ptr, i, f) } + // SAFETY: trivially correct. + unsafe { Self::try_initialize(key, ptr, i, f) } } } + /// # Safety + /// * `key` must be the result of calling `self.key.force()` + /// * `ptr` must be the current value associated with `key`. unsafe fn try_initialize( - &'static self, + key: Key, ptr: *mut Value, i: Option<&mut Option>, f: impl FnOnce() -> T, - ) -> Option<&'static T> { + ) -> *const T { if ptr.addr() == 1 { // destructor is running - return None; + return ptr::null(); } - let value = i.and_then(Option::take).unwrap_or_else(f); - let ptr = Box::into_raw(Box::new(Value { value, key: self })); - // SAFETY: (FIXME: get should actually be safe) - let old = unsafe { self.os.get() as *mut Value }; - // SAFETY: `ptr` is a correct pointer that can be destroyed by the key destructor. - unsafe { - self.os.set(ptr as *mut u8); - } + let value = Box::new(Value { value: i.and_then(Option::take).unwrap_or_else(f), key }); + let ptr = Box::into_raw(value); + + // SAFETY: + // * key came from a `LazyKey` and is thus correct. + // * `ptr` is a correct pointer that can be destroyed by the key destructor. + // * the value is stored under the key that it contains. + let old = unsafe { + let old = get(key) as *mut Value; + set(key, ptr as *mut u8); + old + }; + if !old.is_null() { // If the variable was recursively initialized, drop the old value. // SAFETY: We cannot be inside a `LocalKey::with` scope, as the @@ -119,7 +116,7 @@ impl Key { } // SAFETY: We just created this value above. - unsafe { Some(&(*ptr).value) } + unsafe { &(*ptr).value } } } @@ -136,8 +133,10 @@ unsafe extern "C" fn destroy_value(ptr: *mut u8) { abort_on_dtor_unwind(|| { let ptr = unsafe { Box::from_raw(ptr as *mut Value) }; let key = ptr.key; - unsafe { key.os.set(ptr::without_provenance_mut(1)) }; + // SAFETY: `key` is the TLS key `ptr` was stored under. + unsafe { set(key, ptr::without_provenance_mut(1)) }; drop(ptr); - unsafe { key.os.set(ptr::null_mut()) }; + // SAFETY: `key` is the TLS key `ptr` was stored under. + unsafe { set(key, ptr::null_mut()) }; }); } diff --git a/library/std/src/sys/thread_local/static_local.rs b/library/std/src/sys/thread_local/statik.rs similarity index 64% rename from library/std/src/sys/thread_local/static_local.rs rename to library/std/src/sys/thread_local/statik.rs index 6beda2e718802..a3451ab74e04f 100644 --- a/library/std/src/sys/thread_local/static_local.rs +++ b/library/std/src/sys/thread_local/statik.rs @@ -13,19 +13,14 @@ pub macro thread_local_inner { (@key $t:ty, const $init:expr) => {{ const __INIT: $t = $init; - #[inline] - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn __getit( - _init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { + unsafe { + use $crate::thread::LocalKey; use $crate::thread::local_impl::EagerStorage; - static VAL: EagerStorage<$t> = EagerStorage { value: __INIT }; - $crate::option::Option::Some(&VAL.value) - } - - unsafe { - $crate::thread::LocalKey::new(__getit) + LocalKey::new(|_| { + static VAL: EagerStorage<$t> = EagerStorage { value: __INIT }; + &VAL.value + }) } }}, @@ -34,19 +29,14 @@ pub macro thread_local_inner { #[inline] fn __init() -> $t { $init } - #[inline] - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn __getit( - init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { + unsafe { + use $crate::thread::LocalKey; use $crate::thread::local_impl::LazyStorage; - static VAL: LazyStorage<$t> = LazyStorage::new(); - unsafe { $crate::option::Option::Some(VAL.get(init, __init)) } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) + LocalKey::new(|init| { + static VAL: LazyStorage<$t> = LazyStorage::new(); + VAL.get(init, __init) + }) } }}, ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { @@ -73,16 +63,13 @@ impl LazyStorage { LazyStorage { value: UnsafeCell::new(None) } } - /// Gets a reference to the contained value, initializing it if necessary. + /// Gets a pointer to the TLS value, potentially initializing it with the + /// provided parameters. /// - /// # Safety - /// The returned reference may not be used after reentrant initialization has occurred. + /// The resulting pointer may not be used after reentrant inialialization + /// has occurred. #[inline] - pub unsafe fn get( - &'static self, - i: Option<&mut Option>, - f: impl FnOnce() -> T, - ) -> &'static T { + pub fn get(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { let value = unsafe { &*self.value.get() }; match value { Some(v) => v, @@ -91,11 +78,7 @@ impl LazyStorage { } #[cold] - unsafe fn initialize( - &'static self, - i: Option<&mut Option>, - f: impl FnOnce() -> T, - ) -> &'static T { + fn initialize(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { let value = i.and_then(Option::take).unwrap_or_else(f); // Destroy the old value, after updating the TLS variable as the // destructor might reference it. diff --git a/library/std/src/sys_common/fs.rs b/library/std/src/sys_common/fs.rs index 617ac52e51ca8..acb6713cf1b14 100644 --- a/library/std/src/sys_common/fs.rs +++ b/library/std/src/sys_common/fs.rs @@ -42,7 +42,7 @@ fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { fs::remove_dir(path) } -pub fn try_exists(path: &Path) -> io::Result { +pub fn exists(path: &Path) -> io::Result { match fs::metadata(path) { Ok(_) => Ok(true), Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false), diff --git a/library/std/src/sys_common/io.rs b/library/std/src/sys_common/io.rs index 4a42ff3c618ce..e386c955f3767 100644 --- a/library/std/src/sys_common/io.rs +++ b/library/std/src/sys_common/io.rs @@ -5,12 +5,11 @@ pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { #[cfg(test)] #[allow(dead_code)] // not used on emscripten pub mod test { - use crate::env; - use crate::fs; - use crate::path::{Path, PathBuf}; - use crate::thread; use rand::RngCore; + use crate::path::{Path, PathBuf}; + use crate::{env, fs, thread}; + pub struct TempDir(PathBuf); impl TempDir { diff --git a/library/std/src/sys_common/lazy_box.rs b/library/std/src/sys_common/lazy_box.rs index 63c3316bdeb28..b45b05f63baaa 100644 --- a/library/std/src/sys_common/lazy_box.rs +++ b/library/std/src/sys_common/lazy_box.rs @@ -5,10 +5,8 @@ use crate::marker::PhantomData; use crate::ops::{Deref, DerefMut}; use crate::ptr::null_mut; -use crate::sync::atomic::{ - AtomicPtr, - Ordering::{AcqRel, Acquire}, -}; +use crate::sync::atomic::AtomicPtr; +use crate::sync::atomic::Ordering::{AcqRel, Acquire}; pub(crate) struct LazyBox { ptr: AtomicPtr, diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 3a38ba1100f01..60ee405ecaaa2 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -20,23 +20,13 @@ #[cfg(test)] mod tests; -pub mod backtrace; pub mod fs; pub mod io; pub mod lazy_box; pub mod process; -pub mod thread_local_dtor; pub mod wstr; pub mod wtf8; -cfg_if::cfg_if! { - if #[cfg(target_os = "windows")] { - pub use crate::sys::thread_local_key; - } else { - pub mod thread_local_key; - } -} - cfg_if::cfg_if! { if #[cfg(any( all(unix, not(target_os = "l4re")), diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 95ca67fc2e0b6..25ebeb3502d20 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -1,19 +1,14 @@ #[cfg(test)] mod tests; -use crate::cmp; -use crate::fmt; +use crate::ffi::{c_int, c_void}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; -use crate::mem; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::ptr; use crate::sys::common::small_c_string::run_with_cstr; -use crate::sys::net::netc as c; -use crate::sys::net::{cvt, cvt_gai, cvt_r, init, wrlen_t, Socket}; +use crate::sys::net::{cvt, cvt_gai, cvt_r, init, netc as c, wrlen_t, Socket}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; - -use crate::ffi::{c_int, c_void}; +use crate::{cmp, fmt, mem, ptr}; cfg_if::cfg_if! { if #[cfg(any( @@ -42,6 +37,7 @@ cfg_if::cfg_if! { target_os = "hurd", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", + target_os = "solaris", target_os = "illumos", target_os = "haiku", target_os = "nto"))] { use libc::MSG_NOSIGNAL; } else { diff --git a/library/std/src/sys_common/process.rs b/library/std/src/sys_common/process.rs index 4d295cf0f09d5..5333ee146f7d6 100644 --- a/library/std/src/sys_common/process.rs +++ b/library/std/src/sys_common/process.rs @@ -2,12 +2,10 @@ #![unstable(feature = "process_internals", issue = "none")] use crate::collections::BTreeMap; -use crate::env; use crate::ffi::{OsStr, OsString}; -use crate::fmt; -use crate::io; use crate::sys::pipe::read2; use crate::sys::process::{EnvKey, ExitStatus, Process, StdioPipes}; +use crate::{env, fmt, io}; // Stores a set of changes to an environment #[derive(Clone)] diff --git a/library/std/src/sys_common/thread_local_dtor.rs b/library/std/src/sys_common/thread_local_dtor.rs deleted file mode 100644 index 98382fc6acc23..0000000000000 --- a/library/std/src/sys_common/thread_local_dtor.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! Thread-local destructor -//! -//! Besides thread-local "keys" (pointer-sized non-addressable thread-local store -//! with an associated destructor), many platforms also provide thread-local -//! destructors that are not associated with any particular data. These are -//! often more efficient. -//! -//! This module provides a fallback implementation for that interface, based -//! on the less efficient thread-local "keys". Each platform provides -//! a `thread_local_dtor` module which will either re-export the fallback, -//! or implement something more efficient. - -#![unstable(feature = "thread_local_internals", issue = "none")] -#![allow(dead_code)] - -use crate::cell::RefCell; -use crate::ptr; -use crate::sys_common::thread_local_key::StaticKey; - -pub unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - // The fallback implementation uses a vanilla OS-based TLS key to track - // the list of destructors that need to be run for this thread. The key - // then has its own destructor which runs all the other destructors. - // - // The destructor for DTORS is a little special in that it has a `while` - // loop to continuously drain the list of registered destructors. It - // *should* be the case that this loop always terminates because we - // provide the guarantee that a TLS key cannot be set after it is - // flagged for destruction. - - static DTORS: StaticKey = StaticKey::new(Some(run_dtors)); - // FIXME(joboet): integrate RefCell into pointer to avoid infinite recursion - // when the global allocator tries to register a destructor and just panic - // instead. - type List = RefCell>; - if DTORS.get().is_null() { - let v: Box = Box::new(RefCell::new(Vec::new())); - DTORS.set(Box::into_raw(v) as *mut u8); - } - let list = &*(DTORS.get() as *const List); - match list.try_borrow_mut() { - Ok(mut dtors) => dtors.push((t, dtor)), - Err(_) => rtabort!("global allocator may not use TLS"), - } - - unsafe extern "C" fn run_dtors(mut ptr: *mut u8) { - while !ptr.is_null() { - let list = Box::from_raw(ptr as *mut List).into_inner(); - for (ptr, dtor) in list.into_iter() { - dtor(ptr); - } - ptr = DTORS.get(); - DTORS.set(ptr::null_mut()); - } - } -} diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs deleted file mode 100644 index a9cd26389cd41..0000000000000 --- a/library/std/src/sys_common/thread_local_key.rs +++ /dev/null @@ -1,174 +0,0 @@ -//! OS-based thread local storage for non-Windows systems -//! -//! This module provides an implementation of OS-based thread local storage, -//! using the native OS-provided facilities (think `TlsAlloc` or -//! `pthread_setspecific`). The interface of this differs from the other types -//! of thread-local-storage provided in this crate in that OS-based TLS can only -//! get/set pointer-sized data, possibly with an associated destructor. -//! -//! This module also provides two flavors of TLS. One is intended for static -//! initialization, and does not contain a `Drop` implementation to deallocate -//! the OS-TLS key. The other is a type which does implement `Drop` and hence -//! has a safe interface. -//! -//! Windows doesn't use this module at all; `sys::pal::windows::thread_local_key` -//! gets imported in its stead. -//! -//! # Usage -//! -//! This module should likely not be used directly unless other primitives are -//! being built on. Types such as `thread_local::spawn::Key` are likely much -//! more useful in practice than this OS-based version which likely requires -//! unsafe code to interoperate with. -//! -//! # Examples -//! -//! Using a dynamically allocated TLS key. Note that this key can be shared -//! among many threads via an `Arc`. -//! -//! ```ignore (cannot-doctest-private-modules) -//! let key = Key::new(None); -//! assert!(key.get().is_null()); -//! key.set(1 as *mut u8); -//! assert!(!key.get().is_null()); -//! -//! drop(key); // deallocate this TLS slot. -//! ``` -//! -//! Sometimes a statically allocated key is either required or easier to work -//! with, however. -//! -//! ```ignore (cannot-doctest-private-modules) -//! static KEY: StaticKey = INIT; -//! -//! unsafe { -//! assert!(KEY.get().is_null()); -//! KEY.set(1 as *mut u8); -//! } -//! ``` - -#![allow(non_camel_case_types)] -#![unstable(feature = "thread_local_internals", issue = "none")] -#![allow(dead_code)] - -#[cfg(test)] -mod tests; - -use crate::sync::atomic::{self, AtomicUsize, Ordering}; -use crate::sys::thread_local_key as imp; - -/// A type for TLS keys that are statically allocated. -/// -/// This type is entirely `unsafe` to use as it does not protect against -/// use-after-deallocation or use-during-deallocation. -/// -/// The actual OS-TLS key is lazily allocated when this is used for the first -/// time. The key is also deallocated when the Rust runtime exits or `destroy` -/// is called, whichever comes first. -/// -/// # Examples -/// -/// ```ignore (cannot-doctest-private-modules) -/// use tls::os::{StaticKey, INIT}; -/// -/// // Use a regular global static to store the key. -/// static KEY: StaticKey = INIT; -/// -/// // The state provided via `get` and `set` is thread-local. -/// unsafe { -/// assert!(KEY.get().is_null()); -/// KEY.set(1 as *mut u8); -/// } -/// ``` -pub struct StaticKey { - /// Inner static TLS key (internals). - key: AtomicUsize, - /// Destructor for the TLS value. - /// - /// See `Key::new` for information about when the destructor runs and how - /// it runs. - dtor: Option, -} - -/// Constant initialization value for static TLS keys. -/// -/// This value specifies no destructor by default. -pub const INIT: StaticKey = StaticKey::new(None); - -// Define a sentinel value that is likely not to be returned -// as a TLS key. -#[cfg(not(target_os = "nto"))] -const KEY_SENTVAL: usize = 0; -// On QNX Neutrino, 0 is always returned when currently not in use. -// Using 0 would mean to always create two keys and remote the first -// one (with value of 0) immediately afterwards. -#[cfg(target_os = "nto")] -const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1; - -impl StaticKey { - #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] - pub const fn new(dtor: Option) -> StaticKey { - StaticKey { key: atomic::AtomicUsize::new(KEY_SENTVAL), dtor } - } - - /// Gets the value associated with this TLS key - /// - /// This will lazily allocate a TLS key from the OS if one has not already - /// been allocated. - #[inline] - pub unsafe fn get(&self) -> *mut u8 { - imp::get(self.key()) - } - - /// Sets this TLS key to a new value. - /// - /// This will lazily allocate a TLS key from the OS if one has not already - /// been allocated. - #[inline] - pub unsafe fn set(&self, val: *mut u8) { - imp::set(self.key(), val) - } - - #[inline] - unsafe fn key(&self) -> imp::Key { - match self.key.load(Ordering::Acquire) { - KEY_SENTVAL => self.lazy_init() as imp::Key, - n => n as imp::Key, - } - } - - unsafe fn lazy_init(&self) -> usize { - // POSIX allows the key created here to be KEY_SENTVAL, but the compare_exchange - // below relies on using KEY_SENTVAL as a sentinel value to check who won the - // race to set the shared TLS key. As far as I know, there is no - // guaranteed value that cannot be returned as a posix_key_create key, - // so there is no value we can initialize the inner key with to - // prove that it has not yet been set. As such, we'll continue using a - // value of KEY_SENTVAL, but with some gyrations to make sure we have a non-KEY_SENTVAL - // value returned from the creation routine. - // FIXME: this is clearly a hack, and should be cleaned up. - let key1 = imp::create(self.dtor); - let key = if key1 as usize != KEY_SENTVAL { - key1 - } else { - let key2 = imp::create(self.dtor); - imp::destroy(key1); - key2 - }; - rtassert!(key as usize != KEY_SENTVAL); - match self.key.compare_exchange( - KEY_SENTVAL, - key as usize, - Ordering::Release, - Ordering::Acquire, - ) { - // The CAS succeeded, so we've created the actual key - Ok(_) => key as usize, - // If someone beat us to the punch, use their key instead - Err(n) => { - imp::destroy(key); - n - } - } - } -} diff --git a/library/std/src/sys_common/thread_local_key/tests.rs b/library/std/src/sys_common/thread_local_key/tests.rs deleted file mode 100644 index 48bed31af517c..0000000000000 --- a/library/std/src/sys_common/thread_local_key/tests.rs +++ /dev/null @@ -1,17 +0,0 @@ -use super::StaticKey; -use core::ptr; - -#[test] -fn statik() { - static K1: StaticKey = StaticKey::new(None); - static K2: StaticKey = StaticKey::new(None); - - unsafe { - assert!(K1.get().is_null()); - assert!(K2.get().is_null()); - K1.set(ptr::without_provenance_mut(1)); - K2.set(ptr::without_provenance_mut(2)); - assert_eq!(K1.get() as usize, 1); - assert_eq!(K2.get() as usize, 2); - } -} diff --git a/library/std/src/sys_common/wstr.rs b/library/std/src/sys_common/wstr.rs index 8eae160648502..f9a171fb7d8f5 100644 --- a/library/std/src/sys_common/wstr.rs +++ b/library/std/src/sys_common/wstr.rs @@ -15,7 +15,7 @@ pub struct WStrUnits<'a> { } impl WStrUnits<'_> { - /// Create the iterator. Returns `None` if `lpwstr` is null. + /// Creates the iterator. Returns `None` if `lpwstr` is null. /// /// SAFETY: `lpwstr` must point to a null-terminated wide string that lives /// at least as long as the lifetime of this struct. diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index 38e15f9f54960..277c9506febbb 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -23,16 +23,12 @@ use core::str::next_code_point; use crate::borrow::Cow; use crate::collections::TryReserveError; -use crate::fmt; use crate::hash::{Hash, Hasher}; use crate::iter::FusedIterator; -use crate::mem; -use crate::ops; use crate::rc::Rc; -use crate::slice; -use crate::str; use crate::sync::Arc; use crate::sys_common::AsInner; +use crate::{fmt, mem, ops, slice, str}; const UTF8_REPLACEMENT_CHARACTER: &str = "\u{FFFD}"; @@ -325,6 +321,11 @@ impl Wtf8Buf { self.bytes.shrink_to(min_capacity) } + #[inline] + pub fn leak<'a>(self) -> &'a mut Wtf8 { + unsafe { Wtf8::from_mut_bytes_unchecked(self.bytes.leak()) } + } + /// Returns the number of bytes that this string buffer can hold without reallocating. #[inline] pub fn capacity(&self) -> usize { @@ -469,10 +470,13 @@ impl Wtf8Buf { Wtf8Buf { bytes: bytes.into_vec(), is_known_utf8: false } } - /// Part of a hack to make PathBuf::push/pop more efficient. + /// Provides plumbing to core `Vec::extend_from_slice`. + /// More well behaving alternative to allowing outer types + /// full mutable access to the core `Vec`. #[inline] - pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec { - &mut self.bytes + pub(crate) fn extend_from_slice(&mut self, other: &[u8]) { + self.bytes.extend_from_slice(other); + self.is_known_utf8 = false; } } @@ -594,7 +598,8 @@ impl Wtf8 { /// marked unsafe. #[inline] pub unsafe fn from_bytes_unchecked(value: &[u8]) -> &Wtf8 { - mem::transmute(value) + // SAFETY: start with &[u8], end with fancy &[u8] + unsafe { &*(value as *const [u8] as *const Wtf8) } } /// Creates a mutable WTF-8 slice from a mutable WTF-8 byte slice. @@ -603,7 +608,8 @@ impl Wtf8 { /// marked unsafe. #[inline] unsafe fn from_mut_bytes_unchecked(value: &mut [u8]) -> &mut Wtf8 { - mem::transmute(value) + // SAFETY: start with &mut [u8], end with fancy &mut [u8] + unsafe { &mut *(value as *mut [u8] as *mut Wtf8) } } /// Returns the length, in WTF-8 bytes. @@ -934,8 +940,12 @@ pub fn check_utf8_boundary(slice: &Wtf8, index: usize) { /// Copied from core::str::raw::slice_unchecked #[inline] pub unsafe fn slice_unchecked(s: &Wtf8, begin: usize, end: usize) -> &Wtf8 { - // memory layout of a &[u8] and &Wtf8 are the same - Wtf8::from_bytes_unchecked(slice::from_raw_parts(s.bytes.as_ptr().add(begin), end - begin)) + // SAFETY: memory layout of a &[u8] and &Wtf8 are the same + unsafe { + let len = end - begin; + let start = s.as_bytes().as_ptr().add(begin); + Wtf8::from_bytes_unchecked(slice::from_raw_parts(start, len)) + } } /// Copied from core::str::raw::slice_error_fail diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs index 6a1cc41a8fb04..b57c99a8452a1 100644 --- a/library/std/src/sys_common/wtf8/tests.rs +++ b/library/std/src/sys_common/wtf8/tests.rs @@ -725,3 +725,27 @@ fn wtf8_utf8_boundary_between_surrogates() { string.push(CodePoint::from_u32(0xD800).unwrap()); check_utf8_boundary(&string, 3); } + +#[test] +fn wobbled_wtf8_plus_bytes_isnt_utf8() { + let mut string: Wtf8Buf = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_owned() }; + assert!(!string.is_known_utf8); + string.extend_from_slice(b"some utf-8"); + assert!(!string.is_known_utf8); +} + +#[test] +fn wobbled_wtf8_plus_str_isnt_utf8() { + let mut string: Wtf8Buf = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_owned() }; + assert!(!string.is_known_utf8); + string.push_str("some utf-8"); + assert!(!string.is_known_utf8); +} + +#[test] +fn unwobbly_wtf8_plus_utf8_is_utf8() { + let mut string: Wtf8Buf = Wtf8Buf::from_str("hello world"); + assert!(string.is_known_utf8); + string.push_str("some utf-8"); + assert!(string.is_known_utf8); +} diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index c1b4440e56088..f147c5fdcd146 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -62,7 +62,7 @@ use crate::fmt; /// FOO.set(2); /// /// // each thread starts out with the initial value of 1 -/// let t = thread::spawn(move|| { +/// let t = thread::spawn(move || { /// assert_eq!(FOO.get(), 1); /// FOO.set(3); /// }); @@ -123,7 +123,7 @@ pub struct LocalKey { // trivially devirtualizable by LLVM because the value of `inner` never // changes and the constant should be readonly within a crate. This mainly // only runs into problems when TLS statics are exported across crates. - inner: unsafe fn(Option<&mut Option>) -> Option<&'static T>, + inner: fn(Option<&mut Option>) -> *const T, } #[stable(feature = "std_debug", since = "1.16.0")] @@ -238,9 +238,7 @@ impl LocalKey { issue = "none" )] #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] - pub const unsafe fn new( - inner: unsafe fn(Option<&mut Option>) -> Option<&'static T>, - ) -> LocalKey { + pub const unsafe fn new(inner: fn(Option<&mut Option>) -> *const T) -> LocalKey { LocalKey { inner } } @@ -281,8 +279,7 @@ impl LocalKey { where F: FnOnce(&T) -> R, { - // SAFETY: `inner` is safe to call within the lifetime of the thread - let thread_local = unsafe { (self.inner)(None).ok_or(AccessError)? }; + let thread_local = unsafe { (self.inner)(None).as_ref().ok_or(AccessError)? }; Ok(f(thread_local)) } @@ -304,9 +301,8 @@ impl LocalKey { { let mut init = Some(init); - // SAFETY: `inner` is safe to call within the lifetime of the thread let reference = unsafe { - (self.inner)(Some(&mut init)).expect( + (self.inner)(Some(&mut init)).as_ref().expect( "cannot access a Thread Local Storage value \ during or after destruction", ) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 22215873933d6..59720f77465e1 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -159,25 +159,20 @@ mod tests; use crate::any::Any; -use crate::cell::{OnceCell, UnsafeCell}; -use crate::env; -use crate::ffi::{CStr, CString}; -use crate::fmt; -use crate::io; +use crate::cell::{Cell, OnceCell, UnsafeCell}; +use crate::ffi::CStr; use crate::marker::PhantomData; -use crate::mem::{self, forget}; +use crate::mem::{self, forget, ManuallyDrop}; use crate::num::NonZero; -use crate::panic; -use crate::panicking; use crate::pin::Pin; use crate::ptr::addr_of_mut; -use crate::str; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sync::Arc; use crate::sys::sync::Parker; use crate::sys::thread as imp; use crate::sys_common::{AsInner, IntoInner}; use crate::time::{Duration, Instant}; +use crate::{env, fmt, io, panic, panicking, str}; #[stable(feature = "scoped_threads", since = "1.63.0")] mod scoped; @@ -192,22 +187,14 @@ pub use scoped::{scope, Scope, ScopedJoinHandle}; #[macro_use] mod local; -cfg_if::cfg_if! { - if #[cfg(test)] { - // Avoid duplicating the global state associated with thread-locals between this crate and - // realstd. Miri relies on this. - pub use realstd::thread::{local_impl, AccessError, LocalKey}; - } else { - #[stable(feature = "rust1", since = "1.0.0")] - pub use self::local::{AccessError, LocalKey}; - - // Implementation details used by the thread_local!{} macro. - #[doc(hidden)] - #[unstable(feature = "thread_local_internals", issue = "none")] - pub mod local_impl { - pub use crate::sys::thread_local::*; - } - } +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::local::{AccessError, LocalKey}; + +// Implementation details used by the thread_local!{} macro. +#[doc(hidden)] +#[unstable(feature = "thread_local_internals", issue = "none")] +pub mod local_impl { + pub use crate::sys::thread_local::*; } //////////////////////////////////////////////////////////////////////////////// @@ -487,11 +474,7 @@ impl Builder { amt }); - let my_thread = name.map_or_else(Thread::new_unnamed, |name| unsafe { - Thread::new( - CString::new(name).expect("thread name may not contain interior null bytes"), - ) - }); + let my_thread = name.map_or_else(Thread::new_unnamed, Thread::new); let their_thread = my_thread.clone(); let my_packet: Arc> = Arc::new(Packet { @@ -514,11 +497,10 @@ impl Builder { MaybeDangling(mem::MaybeUninit::new(x)) } fn into_inner(self) -> T { - // SAFETY: we are always initialized. - let ret = unsafe { self.0.assume_init_read() }; // Make sure we don't drop. - mem::forget(self); - ret + let this = ManuallyDrop::new(self); + // SAFETY: we are always initialized. + unsafe { this.0.assume_init_read() } } } impl Drop for MaybeDangling { @@ -539,7 +521,7 @@ impl Builder { let f = f.into_inner(); set_current(their_thread); let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| { - crate::sys_common::backtrace::__rust_begin_short_backtrace(f) + crate::sys::backtrace::__rust_begin_short_backtrace(f) })); // SAFETY: `their_packet` as been built just above and moved by the // closure (it is an Arc<...>) and `my_packet` will be stored in the @@ -561,7 +543,8 @@ impl Builder { let main = Box::new(main); // SAFETY: dynamic size and alignment of the Box remain the same. See below for why the // lifetime change is justified. - let main = unsafe { Box::from_raw(Box::into_raw(main) as *mut (dyn FnOnce() + 'static)) }; + let main = + unsafe { Box::from_raw(Box::into_raw(main) as *mut (dyn FnOnce() + Send + 'static)) }; Ok(JoinInner { // SAFETY: @@ -698,17 +681,22 @@ where } thread_local! { + // Invariant: `CURRENT` and `CURRENT_ID` will always be initialized together. + // If `CURRENT` is initialized, then `CURRENT_ID` will hold the same value + // as `CURRENT.id()`. static CURRENT: OnceCell = const { OnceCell::new() }; + static CURRENT_ID: Cell> = const { Cell::new(None) }; } /// Sets the thread handle for the current thread. /// /// Aborts if the handle has been set already to reduce code size. pub(crate) fn set_current(thread: Thread) { + let tid = thread.id(); // Using `unwrap` here can add ~3kB to the binary size. We have complete // control over where this is called, so just abort if there is a bug. CURRENT.with(|current| match current.set(thread) { - Ok(()) => {} + Ok(()) => CURRENT_ID.set(Some(tid)), Err(_) => rtabort!("thread::set_current should only be called once per thread"), }); } @@ -718,7 +706,28 @@ pub(crate) fn set_current(thread: Thread) { /// In contrast to the public `current` function, this will not panic if called /// from inside a TLS destructor. pub(crate) fn try_current() -> Option { - CURRENT.try_with(|current| current.get_or_init(|| Thread::new_unnamed()).clone()).ok() + CURRENT + .try_with(|current| { + current + .get_or_init(|| { + let thread = Thread::new_unnamed(); + CURRENT_ID.set(Some(thread.id())); + thread + }) + .clone() + }) + .ok() +} + +/// Gets the id of the thread that invokes it. +#[inline] +pub(crate) fn current_id() -> ThreadId { + CURRENT_ID.get().unwrap_or_else(|| { + // If `CURRENT_ID` isn't initialized yet, then `CURRENT` must also not be initialized. + // `current()` will initialize both `CURRENT` and `CURRENT_ID` so subsequent calls to + // `current_id()` will succeed immediately. + current().id() + }) } /// Gets a handle to the thread that invokes it. @@ -835,7 +844,7 @@ pub fn panicking() -> bool { panicking::panicking() } -/// Use [`sleep`]. +/// Uses [`sleep`]. /// /// Puts the current thread to sleep for at least the specified amount of time. /// @@ -1106,7 +1115,7 @@ pub fn park() { forget(guard); } -/// Use [`park_timeout`]. +/// Uses [`park_timeout`]. /// /// Blocks unless or until the current thread's token is made available or /// the specified duration has been reached (may wake spuriously). @@ -1272,10 +1281,52 @@ impl ThreadId { /// The internal representation of a `Thread`'s name. enum ThreadName { Main, - Other(CString), + Other(ThreadNameString), Unnamed, } +// This module ensures private fields are kept private, which is necessary to enforce the safety requirements. +mod thread_name_string { + use core::str; + + use super::ThreadName; + use crate::ffi::{CStr, CString}; + + /// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated. + pub(crate) struct ThreadNameString { + inner: CString, + } + impl core::ops::Deref for ThreadNameString { + type Target = CStr; + fn deref(&self) -> &CStr { + &self.inner + } + } + impl From for ThreadNameString { + fn from(s: String) -> Self { + Self { + inner: CString::new(s).expect("thread name may not contain interior null bytes"), + } + } + } + impl ThreadName { + pub fn as_cstr(&self) -> Option<&CStr> { + match self { + ThreadName::Main => Some(c"main"), + ThreadName::Other(other) => Some(other), + ThreadName::Unnamed => None, + } + } + + pub fn as_str(&self) -> Option<&str> { + // SAFETY: `as_cstr` can only return `Some` for a fixed CStr or a `ThreadNameString`, + // which is guaranteed to be UTF-8. + self.as_cstr().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) }) + } + } +} +pub(crate) use thread_name_string::ThreadNameString; + /// The internal representation of a `Thread` handle struct Inner { name: ThreadName, // Guaranteed to be UTF-8 @@ -1315,25 +1366,20 @@ pub struct Thread { impl Thread { /// Used only internally to construct a thread object without spawning. - /// - /// # Safety - /// `name` must be valid UTF-8. - pub(crate) unsafe fn new(name: CString) -> Thread { - unsafe { Self::new_inner(ThreadName::Other(name)) } + pub(crate) fn new(name: String) -> Thread { + Self::new_inner(ThreadName::Other(name.into())) } pub(crate) fn new_unnamed() -> Thread { - unsafe { Self::new_inner(ThreadName::Unnamed) } + Self::new_inner(ThreadName::Unnamed) } // Used in runtime to construct main thread pub(crate) fn new_main() -> Thread { - unsafe { Self::new_inner(ThreadName::Main) } + Self::new_inner(ThreadName::Main) } - /// # Safety - /// If `name` is `ThreadName::Other(_)`, the contained string must be valid UTF-8. - unsafe fn new_inner(name: ThreadName) -> Thread { + fn new_inner(name: ThreadName) -> Thread { // We have to use `unsafe` here to construct the `Parker` in-place, // which is required for the UNIX implementation. // @@ -1456,15 +1502,11 @@ impl Thread { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] pub fn name(&self) -> Option<&str> { - self.cname().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) }) + self.inner.name.as_str() } fn cname(&self) -> Option<&CStr> { - match &self.inner.name { - ThreadName::Main => Some(c"main"), - ThreadName::Other(other) => Some(&other), - ThreadName::Unnamed => None, - } + self.inner.name.as_cstr() } } @@ -1544,7 +1586,7 @@ struct Packet<'scope, T> { // The type `T` should already always be Send (otherwise the thread could not // have been created) and the Packet is Sync because all access to the // `UnsafeCell` synchronized (by the `join()` boundary), and `ScopeData` is Sync. -unsafe impl<'scope, T: Sync> Sync for Packet<'scope, T> {} +unsafe impl<'scope, T: Send> Sync for Packet<'scope, T> {} impl<'scope, T> Drop for Packet<'scope, T> { fn drop(&mut self) { diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index e2e22e5194f4a..ba27c9220aea5 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -1,10 +1,9 @@ use super::{current, park, Builder, JoinInner, Result, Thread}; -use crate::fmt; -use crate::io; use crate::marker::PhantomData; use crate::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use crate::sync::Arc; +use crate::{fmt, io}; /// A scope to spawn scoped threads in. /// @@ -67,7 +66,7 @@ impl ScopeData { } } -/// Create a scope for spawning scoped threads. +/// Creates a scope for spawning scoped threads. /// /// The function passed to `scope` will be provided a [`Scope`] object, /// through which scoped threads can be [spawned][`Scope::spawn`]. diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index 1fb1333be0e45..aa464d56f95b2 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -1,16 +1,12 @@ use super::Builder; use crate::any::Any; -use crate::mem; use crate::panic::panic_any; -use crate::result; -use crate::sync::{ - atomic::{AtomicBool, Ordering}, - mpsc::{channel, Sender}, - Arc, Barrier, -}; +use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::mpsc::{channel, Sender}; +use crate::sync::{Arc, Barrier}; use crate::thread::{self, Scope, ThreadId}; -use crate::time::Duration; -use crate::time::Instant; +use crate::time::{Duration, Instant}; +use crate::{mem, result}; // !!! These tests are dangerous. If something is buggy, they will hang, !!! // !!! instead of exiting cleanly. This might wedge the buildbots. !!! diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 6f1a354d28a85..ae46670c25e61 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -34,18 +34,17 @@ #[cfg(test)] mod tests; +#[stable(feature = "time", since = "1.3.0")] +pub use core::time::Duration; +#[stable(feature = "duration_checked_float", since = "1.66.0")] +pub use core::time::TryFromFloatSecsError; + use crate::error::Error; use crate::fmt; use crate::ops::{Add, AddAssign, Sub, SubAssign}; use crate::sys::time; use crate::sys_common::{FromInner, IntoInner}; -#[stable(feature = "time", since = "1.3.0")] -pub use core::time::Duration; - -#[stable(feature = "duration_checked_float", since = "1.66.0")] -pub use core::time::TryFromFloatSecsError; - /// A measurement of a monotonically nondecreasing clock. /// Opaque and useful only with [`Duration`]. /// diff --git a/library/std/src/time/tests.rs b/library/std/src/time/tests.rs index 6ed84806e6d37..de36dc4c9fd16 100644 --- a/library/std/src/time/tests.rs +++ b/library/std/src/time/tests.rs @@ -1,8 +1,10 @@ -use super::{Duration, Instant, SystemTime, UNIX_EPOCH}; use core::fmt::Debug; + #[cfg(not(target_arch = "wasm32"))] use test::{black_box, Bencher}; +use super::{Duration, Instant, SystemTime, UNIX_EPOCH}; + macro_rules! assert_almost_eq { ($a:expr, $b:expr) => {{ let (a, b) = ($a, $b); diff --git a/library/std/tests/common/mod.rs b/library/std/tests/common/mod.rs index 1aad6549e76c3..7cf70c725e411 100644 --- a/library/std/tests/common/mod.rs +++ b/library/std/tests/common/mod.rs @@ -1,10 +1,9 @@ #![allow(unused)] -use rand::RngCore; -use std::env; -use std::fs; use std::path::{Path, PathBuf}; -use std::thread; +use std::{env, fs, thread}; + +use rand::RngCore; /// Copied from `std::test_helpers::test_rng`, since these tests rely on the /// seed not being the same for every RNG invocation too. diff --git a/library/std/tests/create_dir_all_bare.rs b/library/std/tests/create_dir_all_bare.rs index fd2a7f906f839..8becf713205ee 100644 --- a/library/std/tests/create_dir_all_bare.rs +++ b/library/std/tests/create_dir_all_bare.rs @@ -2,9 +2,9 @@ //! Note that this test changes the current directory so //! should not be in the same process as other tests. -use std::env; -use std::fs; + use std::path::{Path, PathBuf}; +use std::{env, fs}; mod common; diff --git a/library/std/tests/env.rs b/library/std/tests/env.rs index a1ca85c2145f5..4e472b4ce9953 100644 --- a/library/std/tests/env.rs +++ b/library/std/tests/env.rs @@ -4,9 +4,10 @@ use std::ffi::{OsStr, OsString}; use rand::distributions::{Alphanumeric, DistString}; mod common; -use common::test_rng; use std::thread; +use common::test_rng; + #[track_caller] fn make_rand_name() -> OsString { let n = format!("TEST{}", Alphanumeric.sample_string(&mut test_rng(), 10)); diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs new file mode 100644 index 0000000000000..1535742a83a21 --- /dev/null +++ b/library/std/tests/pipe_subprocess.rs @@ -0,0 +1,41 @@ +#![feature(anonymous_pipe)] + +fn main() { + #[cfg(all(not(miri), any(unix, windows)))] + { + use std::io::Read; + use std::pipe::pipe; + use std::{env, process}; + + if env::var("I_AM_THE_CHILD").is_ok() { + child(); + } else { + parent(); + } + + fn parent() { + let me = env::current_exe().unwrap(); + + let (rx, tx) = pipe().unwrap(); + assert!( + process::Command::new(me) + .env("I_AM_THE_CHILD", "1") + .stdout(tx) + .status() + .unwrap() + .success() + ); + + let mut s = String::new(); + (&rx).read_to_string(&mut s).unwrap(); + drop(rx); + assert_eq!(s, "Heloo,\n"); + + println!("Test pipe_subprocess.rs success"); + } + + fn child() { + println!("Heloo,"); + } + } +} diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index c56c111c37ded..d249eb7d50aa5 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -1,9 +1,6 @@ #![cfg(not(target_env = "sgx"))] -use std::env; -use std::fs; -use std::process; -use std::str; +use std::{env, fs, process, str}; mod common; diff --git a/library/std/tests/run-time-detect.rs b/library/std/tests/run-time-detect.rs index c9b9c54e3d49c..6948670565662 100644 --- a/library/std/tests/run-time-detect.rs +++ b/library/std/tests/run-time-detect.rs @@ -121,10 +121,8 @@ fn x86_all() { println!("avx512bw: {:?}", is_x86_feature_detected!("avx512bw")); println!("avx512cd: {:?}", is_x86_feature_detected!("avx512cd")); println!("avx512dq: {:?}", is_x86_feature_detected!("avx512dq")); - println!("avx512er: {:?}", is_x86_feature_detected!("avx512er")); println!("avx512f: {:?}", is_x86_feature_detected!("avx512f")); println!("avx512ifma: {:?}", is_x86_feature_detected!("avx512ifma")); - println!("avx512pf: {:?}", is_x86_feature_detected!("avx512pf")); println!("avx512vbmi2: {:?}", is_x86_feature_detected!("avx512vbmi2")); println!("avx512vbmi: {:?}", is_x86_feature_detected!("avx512vbmi")); println!("avx512vl: {:?}", is_x86_feature_detected!("avx512vl")); diff --git a/library/std/tests/switch-stdout.rs b/library/std/tests/switch-stdout.rs index 0afe18088fa5f..42011a9b3da62 100644 --- a/library/std/tests/switch-stdout.rs +++ b/library/std/tests/switch-stdout.rs @@ -5,11 +5,10 @@ use std::io::{Read, Write}; mod common; -#[cfg(windows)] -use std::os::windows::io::OwnedHandle; - #[cfg(unix)] use std::os::fd::OwnedFd; +#[cfg(windows)] +use std::os::windows::io::OwnedHandle; #[cfg(unix)] fn switch_stdout_to(file: OwnedFd) -> OwnedFd { diff --git a/library/std/tests/windows.rs b/library/std/tests/windows.rs new file mode 100644 index 0000000000000..dab3182b81872 --- /dev/null +++ b/library/std/tests/windows.rs @@ -0,0 +1,16 @@ +#![cfg(windows)] +//! An external tests + +use std::ffi::OsString; +use std::os::windows::ffi::OsStringExt; +use std::path::PathBuf; + +#[test] +#[should_panic] +fn os_string_must_know_it_isnt_utf8_issue_126291() { + let mut utf8 = PathBuf::from(OsString::from("utf8".to_owned())); + let non_utf8: OsString = + OsStringExt::from_wide(&[0x6e, 0x6f, 0x6e, 0xd800, 0x75, 0x74, 0x66, 0x38]); + utf8.set_extension(&non_utf8); + utf8.into_os_string().into_string().unwrap(); +} diff --git a/library/stdarch b/library/stdarch index df3618d9f3516..a5709ad53ceef 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit df3618d9f35165f4bc548114e511c49c29e1fd9b +Subproject commit a5709ad53ceef016902faf349f0d48aff1826f22 diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 1ddacd92e6b94..7165c3e48af42 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -16,8 +16,8 @@ backtrace = ["std/backtrace"] compiler-builtins-c = ["std/compiler-builtins-c"] compiler-builtins-mem = ["std/compiler-builtins-mem"] compiler-builtins-no-asm = ["std/compiler-builtins-no-asm"] +compiler-builtins-no-f16-f128 = ["std/compiler-builtins-no-f16-f128"] compiler-builtins-mangled-names = ["std/compiler-builtins-mangled-names"] -compiler-builtins-weak-intrinsics = ["std/compiler-builtins-weak-intrinsics"] llvm-libunwind = ["std/llvm-libunwind"] system-llvm-libunwind = ["std/system-llvm-libunwind"] panic-unwind = ["std/panic_unwind"] @@ -27,3 +27,4 @@ profiler = ["std/profiler"] std_detect_file_io = ["std/std_detect_file_io"] std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"] std_detect_env_override = ["std/std_detect_env_override"] +windows_raw_dylib = ["std/windows_raw_dylib"] diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index 0e2409f63ab1a..75cc7c00e389c 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -7,8 +7,6 @@ edition = "2021" getopts = { version = "0.2.21", features = ['rustc-dep-of-std'] } std = { path = "../std" } core = { path = "../core" } -panic_unwind = { path = "../panic_unwind" } -panic_abort = { path = "../panic_abort" } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] libc = { version = "0.2.150", default-features = false } diff --git a/library/test/src/bench.rs b/library/test/src/bench.rs index 64ca13c0d4ed3..b71def3b03223 100644 --- a/library/test/src/bench.rs +++ b/library/test/src/bench.rs @@ -1,18 +1,16 @@ //! Benchmarking module. -use super::{ - event::CompletedTest, - options::BenchMode, - test_result::TestResult, - types::{TestDesc, TestId}, - Sender, -}; -use crate::stats; -use std::cmp; -use std::io; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; +use std::{cmp, io}; + +use super::event::CompletedTest; +use super::options::BenchMode; +use super::test_result::TestResult; +use super::types::{TestDesc, TestId}; +use super::Sender; +use crate::stats; /// An identity function that *__hints__* to the compiler to be maximally pessimistic about what /// `black_box` could do. diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 6ac3b3eaa797b..4ccd825bf8dd3 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -1,11 +1,11 @@ //! Module converting command-line arguments into test configuration. use std::env; +use std::io::{self, IsTerminal}; use std::path::PathBuf; use super::options::{ColorConfig, Options, OutputFormat, RunIgnored}; use super::time::TestTimeOptions; -use std::io::{self, IsTerminal}; #[derive(Debug)] pub struct TestOpts { @@ -200,7 +200,7 @@ Test Attributes: pub fn parse_opts(args: &[String]) -> Option { // Parse matches. let opts = optgroups(); - let binary = args.get(0).map(|c| &**c).unwrap_or("..."); + let binary = args.first().map(|c| &**c).unwrap_or("..."); let args = args.get(1..).unwrap_or(args); let matches = match opts.parse(args) { Ok(m) => m, diff --git a/library/test/src/console.rs b/library/test/src/console.rs index 7e224d60d9dc5..4d4cdcf4d7b6c 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -5,19 +5,19 @@ use std::io; use std::io::prelude::Write; use std::time::Instant; -use super::{ - bench::fmt_bench_samples, - cli::TestOpts, - event::{CompletedTest, TestEvent}, - filter_tests, - formatters::{JsonFormatter, JunitFormatter, OutputFormatter, PrettyFormatter, TerseFormatter}, - helpers::{concurrency::get_concurrency, metrics::MetricMap}, - options::{Options, OutputFormat}, - run_tests, term, - test_result::TestResult, - time::{TestExecTime, TestSuiteExecTime}, - types::{NamePadding, TestDesc, TestDescAndFn}, +use super::bench::fmt_bench_samples; +use super::cli::TestOpts; +use super::event::{CompletedTest, TestEvent}; +use super::formatters::{ + JsonFormatter, JunitFormatter, OutputFormatter, PrettyFormatter, TerseFormatter, }; +use super::helpers::concurrency::get_concurrency; +use super::helpers::metrics::MetricMap; +use super::options::{Options, OutputFormat}; +use super::test_result::TestResult; +use super::time::{TestExecTime, TestSuiteExecTime}; +use super::types::{NamePadding, TestDesc, TestDescAndFn}; +use super::{filter_tests, run_tests, term}; /// Generic wrapper over stdout. pub enum OutputLocation { diff --git a/library/test/src/formatters/json.rs b/library/test/src/formatters/json.rs index 6245aae17c4d7..aa1c50641cb54 100644 --- a/library/test/src/formatters/json.rs +++ b/library/test/src/formatters/json.rs @@ -1,12 +1,12 @@ -use std::{borrow::Cow, io, io::prelude::Write}; +use std::borrow::Cow; +use std::io; +use std::io::prelude::Write; use super::OutputFormatter; -use crate::{ - console::{ConsoleTestDiscoveryState, ConsoleTestState, OutputLocation}, - test_result::TestResult, - time, - types::TestDesc, -}; +use crate::console::{ConsoleTestDiscoveryState, ConsoleTestState, OutputLocation}; +use crate::test_result::TestResult; +use crate::time; +use crate::types::TestDesc; pub(crate) struct JsonFormatter { out: OutputLocation, diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs index a211ebf1ded16..96b432008404b 100644 --- a/library/test/src/formatters/junit.rs +++ b/library/test/src/formatters/junit.rs @@ -1,13 +1,12 @@ -use std::io::{self, prelude::Write}; +use std::io::prelude::Write; +use std::io::{self}; use std::time::Duration; use super::OutputFormatter; -use crate::{ - console::{ConsoleTestDiscoveryState, ConsoleTestState, OutputLocation}, - test_result::TestResult, - time, - types::{TestDesc, TestType}, -}; +use crate::console::{ConsoleTestDiscoveryState, ConsoleTestState, OutputLocation}; +use crate::test_result::TestResult; +use crate::time; +use crate::types::{TestDesc, TestType}; pub struct JunitFormatter { out: OutputLocation, diff --git a/library/test/src/formatters/mod.rs b/library/test/src/formatters/mod.rs index bc6ffebc1d3b2..f1225fecfef1a 100644 --- a/library/test/src/formatters/mod.rs +++ b/library/test/src/formatters/mod.rs @@ -1,11 +1,10 @@ -use std::{io, io::prelude::Write}; +use std::io; +use std::io::prelude::Write; -use crate::{ - console::{ConsoleTestDiscoveryState, ConsoleTestState}, - test_result::TestResult, - time, - types::{TestDesc, TestName}, -}; +use crate::console::{ConsoleTestDiscoveryState, ConsoleTestState}; +use crate::test_result::TestResult; +use crate::time; +use crate::types::{TestDesc, TestName}; mod json; mod junit; diff --git a/library/test/src/formatters/pretty.rs b/library/test/src/formatters/pretty.rs index 22654a3400b44..7089eae4330a0 100644 --- a/library/test/src/formatters/pretty.rs +++ b/library/test/src/formatters/pretty.rs @@ -1,14 +1,12 @@ -use std::{io, io::prelude::Write}; +use std::io; +use std::io::prelude::Write; use super::OutputFormatter; -use crate::{ - bench::fmt_bench_samples, - console::{ConsoleTestDiscoveryState, ConsoleTestState, OutputLocation}, - term, - test_result::TestResult, - time, - types::TestDesc, -}; +use crate::bench::fmt_bench_samples; +use crate::console::{ConsoleTestDiscoveryState, ConsoleTestState, OutputLocation}; +use crate::test_result::TestResult; +use crate::types::TestDesc; +use crate::{term, time}; pub(crate) struct PrettyFormatter { out: OutputLocation, diff --git a/library/test/src/formatters/terse.rs b/library/test/src/formatters/terse.rs index 875c66e5fa32c..534aa2f33110c 100644 --- a/library/test/src/formatters/terse.rs +++ b/library/test/src/formatters/terse.rs @@ -1,15 +1,12 @@ -use std::{io, io::prelude::Write}; +use std::io; +use std::io::prelude::Write; use super::OutputFormatter; -use crate::{ - bench::fmt_bench_samples, - console::{ConsoleTestDiscoveryState, ConsoleTestState, OutputLocation}, - term, - test_result::TestResult, - time, - types::NamePadding, - types::TestDesc, -}; +use crate::bench::fmt_bench_samples; +use crate::console::{ConsoleTestDiscoveryState, ConsoleTestState, OutputLocation}; +use crate::test_result::TestResult; +use crate::types::{NamePadding, TestDesc}; +use crate::{term, time}; // We insert a '\n' when the output hits 100 columns in quiet mode. 88 test // result chars leaves 12 chars for a progress count like " 11704/12853". diff --git a/library/test/src/helpers/concurrency.rs b/library/test/src/helpers/concurrency.rs index b395adcf885ce..b1545cbec438a 100644 --- a/library/test/src/helpers/concurrency.rs +++ b/library/test/src/helpers/concurrency.rs @@ -1,6 +1,8 @@ //! Helper module which helps to determine amount of threads to be used //! during tests execution. -use std::{env, num::NonZero, thread}; + +use std::num::NonZero; +use std::{env, thread}; pub fn get_concurrency() -> usize { if let Ok(value) = env::var("RUST_TEST_THREADS") { diff --git a/library/test/src/helpers/metrics.rs b/library/test/src/helpers/metrics.rs index f77a23e6875b2..bc38969cefb8d 100644 --- a/library/test/src/helpers/metrics.rs +++ b/library/test/src/helpers/metrics.rs @@ -1,4 +1,5 @@ //! Benchmark metrics. + use std::collections::BTreeMap; #[derive(Clone, PartialEq, Debug, Copy)] diff --git a/library/test/src/helpers/shuffle.rs b/library/test/src/helpers/shuffle.rs index 2ac3bfbd4d6f2..14389eb0e37af 100644 --- a/library/test/src/helpers/shuffle.rs +++ b/library/test/src/helpers/shuffle.rs @@ -1,8 +1,9 @@ -use crate::cli::TestOpts; -use crate::types::{TestDescAndFn, TestId, TestName}; use std::hash::{DefaultHasher, Hasher}; use std::time::{SystemTime, UNIX_EPOCH}; +use crate::cli::TestOpts; +use crate::types::{TestDescAndFn, TestId, TestName}; + pub fn get_shuffle_seed(opts: &TestOpts) -> Option { opts.shuffle_seed.or_else(|| { if opts.shuffle { diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 7bd08a0605f83..632f8d161affa 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -24,47 +24,41 @@ #![feature(panic_can_unwind)] #![feature(test)] #![allow(internal_features)] +#![warn(rustdoc::unescaped_backticks)] + +pub use cli::TestOpts; -// Public reexports pub use self::bench::{black_box, Bencher}; pub use self::console::run_tests_console; pub use self::options::{ColorConfig, Options, OutputFormat, RunIgnored, ShouldPanic}; pub use self::types::TestName::*; pub use self::types::*; pub use self::ColorConfig::*; -pub use cli::TestOpts; // Module to be used by rustc to compile tests in libtest pub mod test { - pub use crate::{ - assert_test_result, - bench::Bencher, - cli::{parse_opts, TestOpts}, - filter_tests, - helpers::metrics::{Metric, MetricMap}, - options::{Options, RunIgnored, RunStrategy, ShouldPanic}, - run_test, test_main, test_main_static, - test_result::{TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk}, - time::{TestExecTime, TestTimeOptions}, - types::{ - DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, - TestDescAndFn, TestId, TestName, TestType, - }, + pub use crate::bench::Bencher; + pub use crate::cli::{parse_opts, TestOpts}; + pub use crate::helpers::metrics::{Metric, MetricMap}; + pub use crate::options::{Options, RunIgnored, RunStrategy, ShouldPanic}; + pub use crate::test_result::{TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk}; + pub use crate::time::{TestExecTime, TestTimeOptions}; + pub use crate::types::{ + DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, + TestDescAndFn, TestId, TestName, TestType, }; + pub use crate::{assert_test_result, filter_tests, run_test, test_main, test_main_static}; } -use std::{ - collections::VecDeque, - env, io, - io::prelude::Write, - mem::ManuallyDrop, - panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo}, - process::{self, Command, Termination}, - sync::mpsc::{channel, Sender}, - sync::{Arc, Mutex}, - thread, - time::{Duration, Instant}, -}; +use std::collections::VecDeque; +use std::io::prelude::Write; +use std::mem::ManuallyDrop; +use std::panic::{self, catch_unwind, AssertUnwindSafe, PanicHookInfo}; +use std::process::{self, Command, Termination}; +use std::sync::mpsc::{channel, Sender}; +use std::sync::{Arc, Mutex}; +use std::time::{Duration, Instant}; +use std::{env, io, thread}; pub mod bench; mod cli; @@ -83,6 +77,7 @@ mod types; mod tests; use core::any::Any; + use event::{CompletedTest, TestEvent}; use helpers::concurrency::get_concurrency; use helpers::shuffle::{get_shuffle_seed, shuffle_tests}; @@ -123,7 +118,7 @@ pub fn test_main(args: &[String], tests: Vec, options: Option| { + move |info: &'_ PanicHookInfo<'_>| { if !info.can_unwind() { std::mem::forget(std::io::stderr().lock()); let mut stdout = ManuallyDrop::new(std::io::stdout().lock()); @@ -726,7 +721,7 @@ fn spawn_test_subprocess( fn run_test_in_spawned_subprocess(desc: TestDesc, runnable_test: RunnableTest) -> ! { let builtin_panic_hook = panic::take_hook(); - let record_result = Arc::new(move |panic_info: Option<&'_ PanicInfo<'_>>| { + let record_result = Arc::new(move |panic_info: Option<&'_ PanicHookInfo<'_>>| { let test_result = match panic_info { Some(info) => calc_result(&desc, Err(info.payload()), &None, &None), None => calc_result(&desc, Ok(()), &None, &None), diff --git a/library/test/src/stats.rs b/library/test/src/stats.rs index b33b080126131..71c944afde8d6 100644 --- a/library/test/src/stats.rs +++ b/library/test/src/stats.rs @@ -116,7 +116,7 @@ pub struct Summary { } impl Summary { - /// Construct a new summary of a sample set. + /// Constructs a new summary of a sample set. pub fn new(samples: &[f64]) -> Summary { Summary { sum: samples.sum(), diff --git a/library/test/src/stats/tests.rs b/library/test/src/stats/tests.rs index 3a6e8401bf1ab..4b209dcf214da 100644 --- a/library/test/src/stats/tests.rs +++ b/library/test/src/stats/tests.rs @@ -1,10 +1,11 @@ use super::*; extern crate test; -use self::test::test::Bencher; use std::io; use std::io::prelude::*; +use self::test::test::Bencher; + // Test vectors generated from R, using the script src/etc/stat-test-vectors.r. macro_rules! assert_approx_eq { diff --git a/library/test/src/term.rs b/library/test/src/term.rs index a14b0d4f5a962..e736e85d46966 100644 --- a/library/test/src/term.rs +++ b/library/test/src/term.rs @@ -12,7 +12,8 @@ #![deny(missing_docs)] -use std::io::{self, prelude::*}; +use std::io::prelude::*; +use std::io::{self}; pub(crate) use terminfo::TerminfoTerminal; #[cfg(windows)] diff --git a/library/test/src/term/terminfo/mod.rs b/library/test/src/term/terminfo/mod.rs index 67ba89410cd99..67eec3ca50f48 100644 --- a/library/test/src/term/terminfo/mod.rs +++ b/library/test/src/term/terminfo/mod.rs @@ -1,20 +1,18 @@ //! Terminfo database interface. use std::collections::HashMap; -use std::env; -use std::error; -use std::fmt; use std::fs::File; -use std::io::{self, prelude::*, BufReader}; +use std::io::prelude::*; +use std::io::{self, BufReader}; use std::path::Path; - -use super::color; -use super::Terminal; +use std::{env, error, fmt}; use parm::{expand, Param, Variables}; use parser::compiled::{msys_terminfo, parse}; use searcher::get_dbpath_for_term; +use super::{color, Terminal}; + /// A parsed terminfo database entry. #[allow(unused)] #[derive(Debug)] diff --git a/library/test/src/term/terminfo/parm.rs b/library/test/src/term/terminfo/parm.rs index 2815f6cfc77fe..529ec0c36e4a5 100644 --- a/library/test/src/term/terminfo/parm.rs +++ b/library/test/src/term/terminfo/parm.rs @@ -1,10 +1,10 @@ //! Parameterized string expansion +use std::iter::repeat; + use self::Param::*; use self::States::*; -use std::iter::repeat; - #[cfg(test)] mod tests; @@ -524,7 +524,7 @@ fn format(val: Param, op: FormatOp, flags: Flags) -> Result, String> { } else { let mut s_ = Vec::with_capacity(flags.width); s_.extend(repeat(b' ').take(n)); - s_.extend(s.into_iter()); + s_.extend(s); s = s_; } } diff --git a/library/test/src/term/terminfo/parser/compiled.rs b/library/test/src/term/terminfo/parser/compiled.rs index 5d40b7988b52d..e687b3be41fbf 100644 --- a/library/test/src/term/terminfo/parser/compiled.rs +++ b/library/test/src/term/terminfo/parser/compiled.rs @@ -2,11 +2,12 @@ //! ncurses-compatible compiled terminfo format parsing (term(5)) -use super::super::TermInfo; use std::collections::HashMap; use std::io; use std::io::prelude::*; +use super::super::TermInfo; + #[cfg(test)] mod tests; diff --git a/library/test/src/term/terminfo/searcher.rs b/library/test/src/term/terminfo/searcher.rs index 3e8ccc91ab051..1b29598cf804e 100644 --- a/library/test/src/term/terminfo/searcher.rs +++ b/library/test/src/term/terminfo/searcher.rs @@ -2,14 +2,13 @@ //! //! Does not support hashed database, only filesystem! -use std::env; -use std::fs; use std::path::PathBuf; +use std::{env, fs}; #[cfg(test)] mod tests; -/// Return path to database entry for `term` +/// Returns path to database entry for `term` #[allow(deprecated)] pub(crate) fn get_dbpath_for_term(term: &str) -> Option { let mut dirs_to_search = Vec::new(); diff --git a/library/test/src/term/win.rs b/library/test/src/term/win.rs index 55020141a827d..ce9cad37f306b 100644 --- a/library/test/src/term/win.rs +++ b/library/test/src/term/win.rs @@ -5,8 +5,7 @@ use std::io; use std::io::prelude::*; -use super::color; -use super::Terminal; +use super::{color, Terminal}; /// A Terminal implementation that uses the Win32 Console API. pub(crate) struct WinConsole { @@ -22,6 +21,8 @@ type WORD = u16; type DWORD = u32; type BOOL = i32; type HANDLE = *mut u8; +// https://docs.microsoft.com/en-us/windows/console/getstdhandle +const STD_OUTPUT_HANDLE: DWORD = -11 as _; #[allow(non_snake_case)] #[repr(C)] @@ -99,16 +100,13 @@ impl WinConsole { accum |= color_to_bits(self.background) << 4; unsafe { - // Magic -11 means stdout, from - // https://docs.microsoft.com/en-us/windows/console/getstdhandle - // // You may be wondering, "but what about stderr?", and the answer // to that is that setting terminal attributes on the stdout // handle also sets them for stderr, since they go to the same // terminal! Admittedly, this is fragile, since stderr could be // redirected to a different console. This is good enough for // rustc though. See #13400. - let out = GetStdHandle(-11i32 as DWORD); + let out = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(out, accum); } } @@ -120,9 +118,8 @@ impl WinConsole { let bg; unsafe { let mut buffer_info = MaybeUninit::::uninit(); - if GetConsoleScreenBufferInfo(GetStdHandle(-11i32 as DWORD), buffer_info.as_mut_ptr()) - != 0 - { + let handle = GetStdHandle(STD_OUTPUT_HANDLE); + if GetConsoleScreenBufferInfo(handle, buffer_info.as_mut_ptr()) != 0 { let buffer_info = buffer_info.assume_init(); fg = bits_to_color(buffer_info.wAttributes); bg = bits_to_color(buffer_info.wAttributes >> 4); diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs index bb32c70d66311..c5f4b03bfc96c 100644 --- a/library/test/src/test_result.rs +++ b/library/test/src/test_result.rs @@ -1,16 +1,14 @@ use std::any::Any; -use std::process::ExitStatus; - #[cfg(unix)] use std::os::unix::process::ExitStatusExt; +use std::process::ExitStatus; +pub use self::TestResult::*; use super::bench::BenchSamples; use super::options::ShouldPanic; use super::time; use super::types::TestDesc; -pub use self::TestResult::*; - // Return code for secondary process. // Start somewhere other than 0 so we know the return code means what we think // it means. @@ -19,7 +17,15 @@ pub const TR_OK: i32 = 50; // On Windows we use __fastfail to abort, which is documented to use this // exception code. #[cfg(windows)] -const STATUS_ABORTED: i32 = 0xC0000409u32 as i32; +const STATUS_FAIL_FAST_EXCEPTION: i32 = 0xC0000409u32 as i32; + +// On Zircon (the Fuchsia kernel), an abort from userspace calls the +// LLVM implementation of __builtin_trap(), e.g., ud2 on x86, which +// raises a kernel exception. If a userspace process does not +// otherwise arrange exception handling, the kernel kills the process +// with this return code. +#[cfg(target_os = "fuchsia")] +const ZX_TASK_RETCODE_EXCEPTION_KILL: i32 = -1028; #[derive(Debug, Clone, PartialEq)] pub enum TestResult { @@ -96,7 +102,7 @@ pub fn get_result_from_exit_code( let result = match status.code() { Some(TR_OK) => TestResult::TrOk, #[cfg(windows)] - Some(STATUS_ABORTED) => TestResult::TrFailed, + Some(STATUS_FAIL_FAST_EXCEPTION) => TestResult::TrFailed, #[cfg(unix)] None => match status.signal() { Some(libc::SIGABRT) => TestResult::TrFailed, @@ -105,6 +111,9 @@ pub fn get_result_from_exit_code( } None => unreachable!("status.code() returned None but status.signal() was None"), }, + // Upon an abort, Fuchsia returns the status code ZX_TASK_RETCODE_EXCEPTION_KILL. + #[cfg(target_os = "fuchsia")] + Some(ZX_TASK_RETCODE_EXCEPTION_KILL) => TestResult::TrFailed, #[cfg(not(unix))] None => TestResult::TrFailedMsg(format!("unknown return code")), #[cfg(any(windows, unix))] diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index 43a906ad298d1..ba2f35362c54f 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -1,5 +1,4 @@ use super::*; - use crate::{ console::OutputLocation, formatters::PrettyFormatter, @@ -237,8 +236,9 @@ fn test_should_panic_bad_message() { #[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic_non_string_message_type() { - use crate::tests::TrFailedMsg; use std::any::TypeId; + + use crate::tests::TrFailedMsg; fn f() -> Result<(), String> { std::panic::panic_any(1i32); } diff --git a/library/test/src/time.rs b/library/test/src/time.rs index 7fd69d7f7e73c..02ae050db55bd 100644 --- a/library/test/src/time.rs +++ b/library/test/src/time.rs @@ -5,10 +5,9 @@ //! - Provide helpers for `report-time` and `measure-time` options. //! - Provide newtypes for executions times. -use std::env; -use std::fmt; use std::str::FromStr; use std::time::{Duration, Instant}; +use std::{env, fmt}; use super::types::{TestDesc, TestType}; @@ -24,9 +23,10 @@ pub const TEST_WARN_TIMEOUT_S: u64 = 60; /// Example of the expected format is `RUST_TEST_TIME_xxx=100,200`, where 100 means /// warn time, and 200 means critical time. pub mod time_constants { - use super::TEST_WARN_TIMEOUT_S; use std::time::Duration; + use super::TEST_WARN_TIMEOUT_S; + /// Environment variable for overriding default threshold for unit-tests. pub const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; diff --git a/library/test/src/types.rs b/library/test/src/types.rs index 6a7035a8e2918..c3be3466cb928 100644 --- a/library/test/src/types.rs +++ b/library/test/src/types.rs @@ -4,15 +4,14 @@ use std::borrow::Cow; use std::fmt; use std::sync::mpsc::Sender; -use super::__rust_begin_short_backtrace; -use super::bench::Bencher; -use super::event::CompletedTest; -use super::options; - pub use NamePadding::*; pub use TestFn::*; pub use TestName::*; +use super::bench::Bencher; +use super::event::CompletedTest; +use super::{__rust_begin_short_backtrace, options}; + /// Type of the test according to the [Rust book](https://doc.rust-lang.org/cargo/guide/tests.html) /// conventions. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 544d9fbf1ae0f..b3de71f29f394 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -2,7 +2,6 @@ #![unstable(feature = "panic_unwind", issue = "32837")] #![feature(link_cfg)] #![feature(staged_api)] -#![feature(c_unwind)] #![feature(strict_provenance)] #![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] #![cfg_attr(not(target_env = "msvc"), feature(libc))] diff --git a/library/unwind/src/unwinding.rs b/library/unwind/src/unwinding.rs index 083acaeb56af2..b3460791ce5ca 100644 --- a/library/unwind/src/unwinding.rs +++ b/library/unwind/src/unwinding.rs @@ -28,9 +28,7 @@ pub enum _Unwind_Reason_Code { _URC_FAILURE = 9, // used only by ARM EHABI } pub use _Unwind_Reason_Code::*; - -pub use unwinding::abi::UnwindContext; -pub use unwinding::abi::UnwindException; +pub use unwinding::abi::{UnwindContext, UnwindException}; pub enum _Unwind_Context {} pub use unwinding::custom_eh_frame_finder::{ diff --git a/rust-toolchain.toml b/rust-toolchain.toml index b3927fd5f48c3..c38f7aedc2239 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # standard library we currently track. [toolchain] -channel = "nightly-2024-05-23" +channel = "nightly-2024-08-07" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/scripts/check_kani.sh b/scripts/check_kani.sh new file mode 100644 index 0000000000000..c93499cb7a398 --- /dev/null +++ b/scripts/check_kani.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +set -e + +# Set the working directories +VERIFY_RUST_STD_DIR="$1" +KANI_DIR=$(mktemp -d) + +RUNNER_TEMP=$(mktemp -d) + +# Get the OS name +os_name=$(uname -s) + +# Checkout your local repository +echo "Checking out local repository..." +echo +cd "$VERIFY_RUST_STD_DIR" + +# Checkout the Kani repository +echo "Checking out Kani repository..." +echo +git clone --depth 1 -b features/verify-rust-std https://github.com/model-checking/kani.git "$KANI_DIR" + +# Check the OS and +# Setup dependencies for Kani +echo "Setting up dependencies for Kani..." +echo +cd "$KANI_DIR" +if [ "$os_name" == "Linux" ]; then + ./scripts/setup/ubuntu/install_deps.sh +elif [ "$os_name" == "Darwin" ]; then + ./scripts/setup/macos/install_deps.sh +else + echo "Unknown operating system" +fi + +# Build Kani +echo "Building Kani..." +echo +cargo build-dev --release +# echo "$(pwd)/scripts" >> $PATH + +# Run tests +echo "Running tests..." +echo +cd "$VERIFY_RUST_STD_DIR" +$KANI_DIR/scripts/kani verify-std -Z unstable-options $VERIFY_RUST_STD_DIR/library --target-dir "$RUNNER_TEMP" -Z function-contracts -Z mem-predicates + +echo "Tests completed." +echo + +# Clean up the Kani directory (optional) +rm -rf "$KANI_DIR" +rm -rf "$RUNNER_TEMP" +# rm -rf "$VERIFY_RUST_STD_DIR" diff --git a/scripts/check_rustc.sh b/scripts/check_rustc.sh new file mode 100644 index 0000000000000..610f6157b20ce --- /dev/null +++ b/scripts/check_rustc.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +set -e + +# Set the working directory for your local repository +HEAD_DIR=$1 + +# Temporary directory for upstream repository +TEMP_DIR=$(mktemp -d) + +# Checkout your local repository +echo "Checking out local repository..." +cd "$HEAD_DIR" + +# Get the commit ID from rustc --version +echo "Retrieving commit ID..." +COMMIT_ID=$(rustc --version | sed -e "s/.*(\(.*\) .*/\1/") +echo "$COMMIT_ID for rustc is" + +# Clone the rust-lang/rust repository +echo "Cloning rust-lang/rust repository..." +git clone https://github.com/rust-lang/rust.git "$TEMP_DIR/upstream" + +# Checkout the specific commit +echo "Checking out commit $COMMIT_ID..." +cd "$TEMP_DIR/upstream" +git checkout "$COMMIT_ID" + +# Copy your library to the upstream directory +echo "Copying library to upstream directory..." +rm -rf "$TEMP_DIR/upstream/library" +cp -r "$HEAD_DIR/library" "$TEMP_DIR/upstream" + +# Run the tests +cd "$TEMP_DIR/upstream" +export RUSTFLAGS="--check-cfg cfg(kani) --check-cfg cfg(feature,values(any()))" +echo "Running tests..." +./configure --set=llvm.download-ci-llvm=true +./x test --stage 0 library/std + +echo "Tests completed." + +# Clean up the temporary directory +rm -rf "$TEMP_DIR" diff --git a/scripts/pull_from_upstream.sh b/scripts/pull_from_upstream.sh new file mode 100755 index 0000000000000..183d258837eb8 --- /dev/null +++ b/scripts/pull_from_upstream.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -eux + +cd $1 + +TOOLCHAIN_DATE=$2 +COMMIT_HASH=$3 + +# The checkout and pull itself needs to happen in sync with features/verify-rust-std +# Often times rust is going to be ahead of kani in terms of the toolchain, and both need to point to +# the same commit +SYNC_BRANCH="sync-$TOOLCHAIN_DATE" && echo "--- Fork branch: ${SYNC_BRANCH} ---" +# # 1. Update the upstream/master branch with the latest changes +git fetch upstream +git checkout $COMMIT_HASH + +# # 2. Update the subtree branch +git subtree split --prefix=library --onto subtree/library -b subtree/library +# 3. Update main +git fetch origin +git checkout -b ${SYNC_BRANCH} origin/main +git subtree merge --prefix=library subtree/library --squash + +# TODO: Update origin/subtree/library as well after the process by pushing to it diff --git a/scripts/run_update_with_checks.sh b/scripts/run_update_with_checks.sh new file mode 100755 index 0000000000000..e7419f34d4901 --- /dev/null +++ b/scripts/run_update_with_checks.sh @@ -0,0 +1,185 @@ +#!/bin/bash + +set -eux + +BASE_HOME_DIR="$(pwd)" + +# Set variables for verify-rust-std +# NOTE: This process assumes that verify-rust-std is updated automatically +# and independently +REPO_OWNER="model-checking" +REPO_NAME="kani" +BRANCH_NAME="features/verify-rust-std" +TOOLCHAIN_FILE="rust-toolchain.toml" + +# Set base as verify-rust-std's origin/main branch +BASE_REPO="https://github.com/model-checking/verify-rust-std.git" + +# Create a temporary directory +TEMP_HOME_DIR=$(mktemp -d) + +# Clone the repository into the temporary directory +git clone "$BASE_REPO" "$TEMP_HOME_DIR" +cd $TEMP_HOME_DIR + +# Function to extract commit hash and date from rustc version +get_rustc_info() { + local rustc_output=$(rustc --version --verbose) + local commit_hash=$(echo "$rustc_output" | grep 'commit-hash' | awk '{print $2}') + local commit_date=$(echo "$rustc_output" | grep 'commit-date' | awk '{print $2}') + + if [ -z "$commit_hash" ] || [ -z "$commit_date" ]; then + echo "Error: Could not extract commit hash or date from rustc output." + exit 1 + fi + + echo "$commit_hash:$commit_date" +} + +# Read the toolchain date from the rust-toolchain.toml file +read_toolchain_date() { + local toolchain_file=$TOOLCHAIN_FILE + + if [ ! -f "$toolchain_file" ]; then + echo "Error: $toolchain_file not found in the working directory." >&2 + return 1 + fi + + local toolchain_date + toolchain_date=$(grep -oP 'channel = "nightly-\K\d{4}-\d{2}-\d{2}' ./rust-toolchain.toml) + + if [ -z "$toolchain_date" ]; then + echo "Error: Could not extract date from $toolchain_file" >&2 + return 1 + fi + + echo "$toolchain_date" +} + +# Check if a path is provided as an argument +# This is useful for local processing and debugging +if [ $# -eq 1 ]; then + REPO_PATH="$1" + echo "Using provided repository path: $REPO_PATH" + + # Ensure the provided path exists and is a git repository + if [ ! -d "$REPO_PATH/.git" ]; then + echo "Error: Provided path is not a git repository." + exit 1 + fi + + pushd $REPO_PATH + git switch $BRANCH_NAME + + # Get rustc info + RUSTC_INFO=$(get_rustc_info) + TOOLCHAIN_DATE=$(read_toolchain_date) + + if [ $? -ne 0 ]; then + exit 1 + fi + COMMIT_HASH=$(echo $RUSTC_INFO | cut -d':' -f1) + RUST_DATE=$(echo $RUSTC_INFO | cut -d':' -f2) + popd +else + # Create a temporary directory + TEMP_KANI_DIR=$(mktemp -d) + echo "Created temporary directory: $TEMP_KANI_DIR" + + # Clone the repository into the temporary directory + echo "Cloning repository..." + git clone --branch "$BRANCH_NAME" --depth 1 "https://github.com/$REPO_OWNER/$REPO_NAME.git" "$TEMP_KANI_DIR" + + # Move into this temp dir to read the toolchain + cd $TEMP_KANI_DIR + + if [ $? -ne 0 ]; then + echo "Error: Failed to clone the repository." + rm -rf "$TEMP_KANI_DIR" + exit 1 + fi + + # Get rustc info + RUSTC_INFO=$(get_rustc_info) + TOOLCHAIN_DATE=$(read_toolchain_date) + + COMMIT_HASH=$(echo $RUSTC_INFO | cut -d':' -f1) + RUST_DATE=$(echo $RUSTC_INFO | cut -d':' -f2) + + # Clean up the temporary directory + rm -rf "$TEMP_KANI_DIR" +fi + +if [ -z "$COMMIT_HASH" ]; then + echo "Could not find commit hash in rust-toolchain.toml" + exit 1 +fi + +if [ -z "$RUST_DATE" ]; then + echo "Could not find date in rust-toolchain.toml" + exit 1 +fi + +# Go to temp dir +cd $TEMP_HOME_DIR + +echo "Rust commit hash found: $COMMIT_HASH" +echo "Rust date found: $TOOLCHAIN_DATE" + +# Ensure we have the rust-lang/rust repository as a remote named "upstream" +if ! git remote | grep -q '^upstream$'; then + echo "Adding rust-lang/rust as upstream remote" + git remote add upstream https://github.com/rust-lang/rust.git +fi + + +echo "------------------------------------" +# Call the first script to update the subtree +echo "Update subtree in Main" +source $BASE_HOME_DIR/scripts/pull_from_upstream.sh "$TEMP_HOME_DIR" $TOOLCHAIN_DATE $COMMIT_HASH +OUTPUT_SCRIPT1=("$?") +if [ "${#OUTPUT_SCRIPT1[@]}" -eq 0 ]; then + echo "script1.sh failed to run." + exit 1 +else + echo "script1.sh completed successfully." +fi + +# Call the second script +echo "Running script2.sh..." +source $BASE_HOME_DIR/scripts/update_toolchain_date.sh "$TEMP_HOME_DIR" "$TOOLCHAIN_DATE" +OUTPUT_SCRIPT2=("$?") +if [ "${#OUTPUT_SCRIPT2[@]}" -eq 0 ]; then + echo "script2.sh failed to run." + exit 1 +else + echo "Update toolchain ran successfully." +fi + +# Call the third script +echo "Running script3.sh..." +source $BASE_HOME_DIR/scripts/check_rustc.sh "$TEMP_HOME_DIR" +OUTPUT_SCRIPT3=("$?") +if [ "${#OUTPUT_SCRIPT3[@]}" -eq 0 ]; then + echo "script3.sh failed to run." + exit 1 +else + echo "script3.sh completed successfully." +fi + +# Call the fourth script +echo "Running script4.sh..." +source $BASE_HOME_DIR/scripts/check_kani.sh "$TEMP_HOME_DIR" +OUTPUT_SCRIPT4=("$?") +if [ "${#OUTPUT_SCRIPT4[@]}" -eq 0 ]; then + echo "script4.sh failed to run." + exit 1 +else + echo "script4.sh completed successfully." +fi + +# TODO: Issue a Pull Request from the sync branch of the temp repo +# cd $TEMP_HOME_DIR + +# Remove the temporary directory +# rm -rf "$TEMP_DIR" diff --git a/scripts/update_toolchain_date.sh b/scripts/update_toolchain_date.sh new file mode 100755 index 0000000000000..21798932dfd3f --- /dev/null +++ b/scripts/update_toolchain_date.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Check if the correct number of args +if [[ $# -ne 2 ]]; then + echo "Usage: $0 " + echo "$#" + exit 1 +fi + +toolchain_file="$1/rust-toolchain.toml" +new_date="$2" + +# Check if the toolchain file exists +if [[ ! -f "$toolchain_file" ]]; then + echo "Error: Toolchain file does not exist." + exit 1 +fi + +# Use sed to replace the date +sed -i.bak -E 's/^channel = "nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}"$/channel = "nightly-'"$new_date"'"/' "$toolchain_file" + +# Check if the replacement was succesful +if [[ $? -eq 0 ]]; then + echo "Date succesfully updated in $toolchain_file" + rm "${toolchain_file}.bak" + + git commit -am "Update toolchain to $new_date" +else + echo "Error: Failed to update the file in $toolchain_file" + exit 1 +fi