diff --git a/.github/TOOL_REQUEST_TEMPLATE.md b/.github/TOOL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000000..474b1d089c6ee --- /dev/null +++ b/.github/TOOL_REQUEST_TEMPLATE.md @@ -0,0 +1,31 @@ +_The following form is designed to provide information for your tool that should be included in the effort to verify the Rust standard library. Please note that the tool will need to be **supported** if it is to be included._ + +## Tool Name +_Please enter your tool name here._ + +## Description +_Please enter a description for your tool and any information you deem relevant._ + +## Tool Information + +* [ ] Does the tool perform Rust verification? +* [ ] Does the tool deal with *unsafe* Rust code? +* [ ] Does the tool run independently in CI? +* [ ] Is the tool open source? +* [ ] Is the tool under development? +* [ ] Will you or your team be able to provide support for the tool? + +## Licenses +_Please list the license(s) that are used by your tool, and if to your knowledge they conflict with the Rust standard library license(s)._ + +## Steps to Use the Tool + +1. [First Step] +2. [Second Step] +3. [and so on...] + +## Artifacts +_If there are noteworthy examples of using the tool to perform verificaiton, please include them in this section.Links, papers, etc._ + +## CI & Versioning +_Please describe how you version the tool and how it will be supported in CI pipelines._ diff --git a/.github/pull_requests.toml b/.github/pull_requests.toml index 12113728fc722..45a38b3cc2307 100644 --- a/.github/pull_requests.toml +++ b/.github/pull_requests.toml @@ -9,5 +9,8 @@ members = [ "remi-delmas-3000", "qinheping", "tautschnig", - "jaisnan" + "jaisnan", + "patricklam", + "ranjitjhala", + "carolynzech" ] 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..f261da3ad2ac2 100644 --- a/.github/workflows/pr_approval.yml +++ b/.github/workflows/pr_approval.yml @@ -7,16 +7,9 @@ name: Check PR Approvals on: pull_request_review: types: [submitted] - workflow_dispatch: - -# Without these permissions, we get a 403 error from github -# for trying to modify the pull request for newer project. -# Source: https://stackoverflow.com/a/76994510 -permissions: write-all jobs: check-approvals: - if: github.event.review.state == 'APPROVED' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest steps: - name: Checkout repository @@ -51,22 +44,6 @@ jobs: pull_number = context.issue.number; } - // Get PR files - const files = await github.rest.pulls.listFiles({ - owner, - repo, - pull_number - }); - - const relevantPaths = ['library/', 'doc/src/challenges/']; - const isRelevantPR = files.data.some(file => - relevantPaths.some(path => file.filename.startsWith(path)) - ); - - if (!isRelevantPR) { - console.log('PR does not touch relevant paths. Exiting workflow.'); - return; - } // Get parsed data try { @@ -117,45 +94,6 @@ jobs: text: `Approvers: ${Array.from(approvers).join(', ')}\nRequired Approvers: ${requiredApprovers.join(', ')}` }; - // Get PR details - const pr = await github.rest.pulls.get({ - owner, - repo, - pull_number - }); - - // Create or update check run - const checkRuns = await github.rest.checks.listForRef({ - owner, - repo, - ref: pr.data.head.sha, - 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 - }); - } - 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/README.md b/README.md index b718f3fc6b1cd..c3c9712cb2669 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ This repository is a fork of the official Rust programming language repository, created solely to verify the Rust standard library. It should not be used as an alternative to the official -Rust releases. +Rust releases. The repository is tool agnostic and welcomes the addition of +new tools. The goal is to have a verified [Rust standard library](https://doc.rust-lang.org/std/) and prove that it is safe. 1. Contributing to the core mechanism of verifying the rust standard library @@ -36,12 +37,14 @@ See [SECURITY](https://github.com/model-checking/kani/security/policy) for more ## License ### Kani - Kani is distributed under the terms of both the MIT license and the Apache License (Version 2.0). See [LICENSE-APACHE](https://github.com/model-checking/kani/blob/main/LICENSE-APACHE) and [LICENSE-MIT](https://github.com/model-checking/kani/blob/main/LICENSE-MIT) for details. ## Rust - Rust is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0), with portions covered by various BSD-like licenses. See [the Rust repository](https://github.com/rust-lang/rust) for details. + +## Introducing a New Tool + +Please use the [template available in this repository](.github/TOOL_REQUEST_TEMPLATE.md) to introduce a new verification tool. diff --git a/doc/book.toml b/doc/book.toml index 7f5e1329fef4d..078022e148f1f 100644 --- a/doc/book.toml +++ b/doc/book.toml @@ -12,6 +12,7 @@ build-dir = "../book" site-url = "/verify-rust-std/" git-repository-url = "https://github.com/model-checking/verify-rust-std" edit-url-template = "https://github.com/model-checking/verify-rust-std/edit/main/doc/{path}" +no-section-label = true [output.html.playground] runnable = false diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 9a8023d63908a..db5018121be02 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -13,8 +13,16 @@ --- - [Challenges](./challenges.md) - - [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) + - [1: Verify core transmuting methods](./challenges/0001-core-transmutation.md) + - [2: Verify the memory safery of core intrinsics using raw pointers](./challenges/0002-intrinsics-memory.md) + - [3: Verifying Raw Pointer Arithmetic Operations](./challenges/0003-pointer-arithmentic.md) + - [4: Memory safety of BTreeMap's `btree::node` module](./challenges/0004-btree-node.md) + - [5: Verify functions iterating over inductive data type: `linked_list`](./challenges/0005-linked-list.md) + - [6: Safety of `NonNull`](./challenges/0006-nonnull.md) + - [8: Contracts for SmallSort](./challenges/0008-smallsort.md) + - [9: Safe abstractions for `core::time::Duration`](./challenges/0009-duration.md) + - [10: Memory safety of String](./challenges/0010-string.md) + - [11: Safety of Methods for Numeric Primitive Types](./challenges/0011-floats-ints.md) + - [12: Safety of `NonZero`](./challenges/0012-nonzero.md) + + diff --git a/doc/src/challenges/0001-core-transmutation.md b/doc/src/challenges/0001-core-transmutation.md index 3a71c0c12ff29..2b53256b3c34e 100644 --- a/doc/src/challenges/0001-core-transmutation.md +++ b/doc/src/challenges/0001-core-transmutation.md @@ -1,7 +1,7 @@ # Challenge 1: Verify `core` transmuting methods - **Status:** Open -- **Tracking Issue:** [Link to issue](https://github.com/model-checking/verify-rust-std/issues/19) +- **Tracking Issue:** [#19](https://github.com/model-checking/verify-rust-std/issues/19) - **Start date:** 2024-06-12 - **End date:** 2024-12-10 diff --git a/doc/src/challenges/0002-intrinsics-memory.md b/doc/src/challenges/0002-intrinsics-memory.md index 62ee3a8c4dab6..ec086b5d73dc5 100644 --- a/doc/src/challenges/0002-intrinsics-memory.md +++ b/doc/src/challenges/0002-intrinsics-memory.md @@ -1,7 +1,7 @@ # Challenge 2: Verify the memory safery of core intrinsics using raw pointers - **Status:** Open -- **Tracking Issue:** [Link to issue](https://github.com/model-checking/verify-rust-std/issues/16) +- **Tracking Issue:** [#16](https://github.com/model-checking/verify-rust-std/issues/16) - **Start date:** *24/06/12* - **End date:** *24/12/10* diff --git a/doc/src/challenges/0003-pointer-arithmentic.md b/doc/src/challenges/0003-pointer-arithmentic.md index 0e73c3c7f014a..5362983ffe0f3 100644 --- a/doc/src/challenges/0003-pointer-arithmentic.md +++ b/doc/src/challenges/0003-pointer-arithmentic.md @@ -2,7 +2,7 @@ - **Status:** Open - **Solution:** -- **Tracking Issue:** +- **Tracking Issue:** [#76](https://github.com/model-checking/verify-rust-std/issues/76) - **Start date:** 24/06/24 - **End date:** 24/12/10 diff --git a/doc/src/challenges/0004-btree-node.md b/doc/src/challenges/0004-btree-node.md index 6f29ce23eba73..835d71365292f 100644 --- a/doc/src/challenges/0004-btree-node.md +++ b/doc/src/challenges/0004-btree-node.md @@ -1,7 +1,7 @@ # 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) +- **Tracking Issue:** [#77](https://github.com/model-checking/verify-rust-std/issues/77) - **Start date:** *2024-07-01* - **End date:** *2024-12-10* diff --git a/doc/src/challenges/0005-linked-list.md b/doc/src/challenges/0005-linked-list.md index 5701acb7b82d2..a5c931712025a 100644 --- a/doc/src/challenges/0005-linked-list.md +++ b/doc/src/challenges/0005-linked-list.md @@ -1,7 +1,7 @@ # Challenge 5: Verify functions iterating over inductive data type: `linked_list` - **Status:** Open -- **Tracking Issue:** [Link to issue](https://github.com/model-checking/verify-rust-std/issues/29) +- **Tracking Issue:** [#29](https://github.com/model-checking/verify-rust-std/issues/29) - **Start date:** *24/07/01* - **End date:** *24/12/10* diff --git a/doc/src/challenges/0006-nonnull.md b/doc/src/challenges/0006-nonnull.md new file mode 100644 index 0000000000000..923e00427ad62 --- /dev/null +++ b/doc/src/challenges/0006-nonnull.md @@ -0,0 +1,84 @@ +# Challenge 6: Safety of NonNull + +- **Status:** Open +- **Tracking Issue:** [#53](https://github.com/model-checking/verify-rust-std/issues/53) +- **Start date:** *2024-08-16* +- **End date:** *2024-12-10* + +------------------- + + +## Goal + +Verify absence of undefined behavior of the [`ptr::NonNull` module](https://github.com/rust-lang/rust/blob/master/library/core/src/ptr/non_null.rs). +Most of its functions are marked `unsafe`, yet they are used in 62 other modules +of the standard library. + +### Success Criteria + +Prove absence of undefined behavior of the following 48 public functions. You +may wish to do so by attaching pre- and postconditions to these, and then (if +needed by the tooling that you choose to use) adding verification harnesses. + +1. `NonNull::add` +2. `NonNull::addr` +3. `NonNull::align_offset` +4. `NonNull::as_mut<'a>` +5. `NonNull::as_mut_ptr` +6. `NonNull::as_non_null_ptr` +7. `NonNull::as_ptr` +8. `NonNull::as_ref<'a>` +9. `NonNull::as_uninit_mut<'a>` +10. `NonNull::as_uninit_ref<'a>` +11. `NonNull::as_uninit_slice<'a>` +12. `NonNull::as_uninit_slice_mut<'a>` +13. `NonNull::byte_add` +14. `NonNull::byte_offset_from` +15. `NonNull::byte_offset` +16. `NonNull::byte_sub` +17. `NonNull::cast` +18. `NonNull::copy_from_nonoverlapping` +19. `NonNull::copy_from` +20. `NonNull::copy_to_nonoverlapping` +21. `NonNull::copy_to` +22. `NonNull::dangling` +23. `NonNull::drop_in_place` +24. `NonNull::from_raw_parts` +25. `NonNull::get_unchecked_mut` +26. `NonNull::is_aligned_to` +27. `NonNull::is_aligned` +28. `NonNull::is_empty` +29. `NonNull::len` +30. `NonNull::map_addr` +31. `NonNull::new_unchecked` +32. `NonNull::new` +33. `NonNull::offset_from` +34. `NonNull::offset` +35. `NonNull::read_unaligned` +36. `NonNull::read_volatile` +37. `NonNull::read` +38. `NonNull::replace` +39. `NonNull::slice_from_raw_parts` +40. `NonNull::sub_ptr` +41. `NonNull::sub` +42. `NonNull::swap` +43. `NonNull::to_raw_parts` +44. `NonNull::with_addr` +45. `NonNull::write_bytes` +46. `NonNull::write_unaligned` +47. `NonNull::write_volatile` +48. `NonNull::write` + +### 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/0008-smallsort.md b/doc/src/challenges/0008-smallsort.md new file mode 100644 index 0000000000000..c6632af9af837 --- /dev/null +++ b/doc/src/challenges/0008-smallsort.md @@ -0,0 +1,54 @@ +# Challenge 8: Contracts for SmallSort + +- **Status:** Open +- **Tracking Issue:** [#56](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/0009-duration.md b/doc/src/challenges/0009-duration.md new file mode 100644 index 0000000000000..e2af60ec6c1bb --- /dev/null +++ b/doc/src/challenges/0009-duration.md @@ -0,0 +1,72 @@ +# Challenge 9: Safe abstractions for `core::time::Duration` + +- **Status:** Open +- **Tracking Issue:** [#72](https://github.com/model-checking/verify-rust-std/issues/72) +- **Start date:** *2024-08-20* +- **End date:** *2024-12-20* + +------------------- + + +## Goal + +Write function contracts for `core::time::Duration` that can be used as safe abstractions. +Even though the majority of `Duration` methods are safe, many of them are safe abstractions over unsafe code. + +For instance, the `new` method is implemented as follows in v1.3.0: +```rust +pub const fn new(secs: u64, nanos: u32) -> Duration { + if nanos < NANOS_PER_SEC { + // SAFETY: nanos < NANOS_PER_SEC, therefore nanos is within the valid range + Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } + } else { + let secs = match secs.checked_add((nanos / NANOS_PER_SEC) as u64) { + Some(secs) => secs, + None => panic!("overflow in Duration::new"), + }; + let nanos = nanos % NANOS_PER_SEC; + // SAFETY: nanos % NANOS_PER_SEC < NANOS_PER_SEC, therefore nanos is within the valid range + Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } + } +} +``` + +### Success Criteria + +Write a [type invariant](https://model-checking.github.io/kani/crates/doc/kani/derive.Invariant.html) for the struct `Duration`. Write function contracts for the following public functions. + +1. `Duration::new(secs: u64, nanos: u32) -> Duration` +2. `Duration::from_secs(secs: u64) -> Duration` +3. `Duration::from_millis(millis: u64) -> Duration` +4. `Duration::from_micros(micros: u64) -> Duration` +5. `Duration::from_nanos(nanos: u64) -> Duration` + +6. `Duration::as_secs(&self) -> u64` +7. `Duration::as_millis(&self) -> u128` +8. `Duration::as_micros(&self) -> u128` +9. `Duration::as_nanos(&self) -> u128` +10. `Duration::subsec_millis(&self) -> u32` +11. `Duration::subsec_micros(&self) -> u32` +12. `Duration::subsec_nanos(&self) -> u32` + +13. `Duration::checked_add(&self, rhs: Duration) -> Option` +14. `Duration::checked_sub(&self, rhs: Duration) -> Option` +15. `Duration::checked_mul(&self, rhs: u32) -> Option` +16. `Duration::checked_div(&self, rhs: u32) -> Option` + +The memory safety and the contracts of the above listed functions must be verified +for all possible input values. + +### 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..4783841bee429 --- /dev/null +++ b/doc/src/challenges/0010-string.md @@ -0,0 +1,75 @@ +# Challenge 10: Memory safety of String + +- **Status:** Open +- **Tracking Issue:** [#61](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/doc/src/challenges/0011-floats-ints.md b/doc/src/challenges/0011-floats-ints.md new file mode 100644 index 0000000000000..9dde411c527ca --- /dev/null +++ b/doc/src/challenges/0011-floats-ints.md @@ -0,0 +1,59 @@ +# Challenge 11: Safety of Methods for Numeric Primitive Types + + +- **Status:** Open +- **Tracking Issue:** [#59](https://github.com/model-checking/verify-rust-std/issues/59) +- **Start date:** *2024-08-20* +- **End date:** *2024-12-10* + +------------------- + +## Goal + +Verify the safety of public unsafe methods for floats and integers in `core::num`. + +To find the documentation for these methods, navigate first to the [`core::num` documentation](https://doc.rust-lang.org/core/num/index.html), then click on the appropriate primitive type in the left-side menu bar. For example, for `i8::unchecked_add`, click on `i8`. + +## Success Criteria + +### Part 1: Unsafe Integer Methods + +Prove the absence of arithmetic overflow/underflow and undefined behavior in the following methods for each of the listed integer types, given that their safety preconditions are satisfied: + +| Method | Integer Types | +| :--- | :--- +| `unchecked_add` | `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, `u128` | +| `unchecked_sub` | `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, `u128` | +| `unchecked_mul` | `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, `u128` | +| `unchecked_shl` | `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, `u128` | +| `unchecked_shr` | `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, `u128` | +| `unchecked_neg` | `i8`, `i16`, `i32`, `i64`, `i128` | + + +### Part 2: Safe API Verification + +Now, verify some of the safe APIs that leverage the unsafe integer methods from Part 1. Verify the absence of arithmetic overflow/underflow and undefined behavior of the following methods for each of the listed integer types: + +| Method | Integer Types | +| :--- | :--- +| `wrapping_shl` | `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, `u128` | +| `wrapping_shr` | `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, `u128` | +| `widening_mul` | `u8`, `u16`, `u32`, `u64` | +| `carrying_mul` | `u8`, `u16`, `u32`, `u64` | + + +### Part 3: Float to Integer Conversion + +Verify the absence of arithmetic overflow/underflow and undefined behavior in `to_int_unchecked` for `f16`, `f32`, `f64`, and `f128`. + + +## 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): + +* Invoking undefined behavior via compiler intrinsics. +* 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/0012-nonzero.md b/doc/src/challenges/0012-nonzero.md new file mode 100644 index 0000000000000..15d2abbb8eb3c --- /dev/null +++ b/doc/src/challenges/0012-nonzero.md @@ -0,0 +1,87 @@ +# Challenge 12: Safety of `NonZero` + +- **Status:** Open +- **Tracking Issue:** [#71](https://github.com/model-checking/verify-rust-std/issues/71) +- **Start date:** *2024-08-23* +- **End date:** *2024-12-10* + +------------------- + +## Goal + +Verify the safety of `NonZero` in `core::num`. + +### Assumptions + +`new` and `get` leverage `transmute_unchecked`, so verifying the safety of these methods would require verifying that transmutations are safe. This task is out of scope for this challenge (instead, it's work for [Challenge 1](0001-core-transmutation.md)). For this challenge, for a transmutation from type `T` to type `U`, it suffices to write and verify a contract that `T` and `U` have the same size. + +You may assume that each `NonZeroInner` type upholds the safety conditions of the `ZeroablePrimitive` trait. Specifically, you need not verify that the integer primitives which implement `ZeroablePrimitive` are valid when 0, or that transmutations to the `Option` type are sound. + +### Success Criteria + +#### Part 1: `new` and `new_unchecked` + +Verify the safety and correctness of `NonZero::new` and `NonZero::new_unchecked`. + +Specifically, write and verify contracts specifying the following: +1. The preconditions specified by the `SAFETY` comments are upheld. +2. For an input `n`: + a. A `NonZero` object is created if and only if the input was nonzero. + b. The value of the `NonZeroInner` object equals `n`. + +#### Part 2: Other Uses of `unsafe` + +Verify the safety of the following functions and methods (all located within `core::num::nonzero`): + +| Function | +|--------- | +| `max` | +| `min` | +| `clamp` | +| `bitor` (all 3 implementations) | +| `count_ones` | +| `rotate_left` | +| `rotate_right` | +| `swap_bytes` | +| `reverse_bits` | +| `from_be` | +| `from_le` | +| `to_be` | +| `to_le` | +| `checked_mul` | +| `saturating_mul` | +| `unchecked_mul` | +| `checked_pow` | +| `saturating_pow` | +| `neg` | +| `checked_add` | +| `saturating_add` | +| `unchecked_add` | +| `checked_next_power_of_two` | +| `midpoint` | +| `isqrt` | +| `abs` | +| `checked_abs` | +| `overflowing_abs` | +| `saturating_abs` | +| `wrapping_abs` | +| `unsigned_abs` | +| `checked_neg` | +| `overflowing_neg` | +| `wrapping_neg` | +| `from_mut` | +| `from_mut_unchecked` | + +You are not required to write correctness contracts for these methods (e.g., for `max`, ensuring that the `result` is indeed the maximum of the inputs), but it would be great to do so! + +### 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 [ref](https://github.com/rust-lang/reference/blob/142b2ed77d33f37a9973772bd95e6144ed9dce43/src/behavior-considered-undefined.md): + +* Invoking undefined behavior via compiler intrinsics. +* Reading from uninitialized memory. +* 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..54ad052c52322 --- /dev/null +++ b/library/Cargo.lock @@ -0,0 +1,494 @@ +# 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.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b47fcbecb558bdad78c7d3a998523c60a50dd6cd046d5fe74163e309e878fff7" +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.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +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", + "windows-targets 0.0.0", +] + +[[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 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.0.0" + +[[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..e744cfe5e0f57 --- /dev/null +++ b/library/Cargo.toml @@ -0,0 +1,47 @@ +[workspace] +resolver = "1" +members = [ + "std", + "sysroot", +] + +exclude = [ + # stdarch has its own Cargo workspace + "stdarch", + "windows_targets" +] + +[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 +addr2line.opt-level = "s" +adler.debug = 0 +gimli.debug = 0 +gimli.opt-level = "s" +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 612452a960a37..1bd4434d4f7e9 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "0.1.40", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.123", features = ['rustc-dep-of-std'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } @@ -38,8 +38,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 @@ -52,4 +52,5 @@ check-cfg = [ 'cfg(no_global_oom_handling)', 'cfg(no_rc)', 'cfg(no_sync)', + 'cfg(randomized_layouts)', ] 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/lib.rs b/library/alloc/benches/lib.rs index 0561f49c967e5..ae9608ec7bd5c 100644 --- a/library/alloc/benches/lib.rs +++ b/library/alloc/benches/lib.rs @@ -1,6 +1,3 @@ -// Disabling on android for the time being -// See https://github.com/rust-lang/rust/issues/73535#event-3477699747 -#![cfg(not(target_os = "android"))] // Disabling in Miri as these would take too long. #![cfg(not(miri))] #![feature(btree_extract_if)] 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 1833a7f477f00..cddf4f6f39963 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, @@ -374,6 +372,7 @@ extern "Rust" { #[rustc_const_unstable(feature = "const_alloc_error", issue = "92523")] #[cfg(all(not(no_global_oom_handling), not(test)))] #[cold] +#[optimize(size)] pub const fn handle_alloc_error(layout: Layout) -> ! { const fn ct_error(_: Layout) -> ! { panic!("allocation failed"); 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 f299aa0124dbe..6dc75478700ce 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -53,22 +53,20 @@ //! //! # Memory layout //! -//! For non-zero-sized values, a [`Box`] will use the [`Global`] allocator for -//! its allocation. It is valid to convert both ways between a [`Box`] and a -//! raw pointer allocated with the [`Global`] allocator, given that the -//! [`Layout`] used with the allocator is correct for the type. More precisely, -//! a `value: *mut T` that has been allocated with the [`Global`] allocator -//! with `Layout::for_value(&*value)` may be converted into a box using -//! [`Box::::from_raw(value)`]. Conversely, the memory backing a `value: *mut -//! T` obtained from [`Box::::into_raw`] may be deallocated using the -//! [`Global`] allocator with [`Layout::for_value(&*value)`]. +//! For non-zero-sized values, a [`Box`] will use the [`Global`] allocator for its allocation. It is +//! valid to convert both ways between a [`Box`] and a raw pointer allocated with the [`Global`] +//! allocator, given that the [`Layout`] used with the allocator is correct for the type and the raw +//! pointer points to a valid value of the right type. More precisely, a `value: *mut T` that has +//! been allocated with the [`Global`] allocator with `Layout::for_value(&*value)` may be converted +//! into a box using [`Box::::from_raw(value)`]. Conversely, the memory backing a `value: *mut T` +//! obtained from [`Box::::into_raw`] may be deallocated using the [`Global`] allocator with +//! [`Layout::for_value(&*value)`]. //! -//! For zero-sized values, the `Box` pointer still has to be [valid] for reads -//! and writes and sufficiently aligned. In particular, casting any aligned -//! non-zero integer literal to a raw pointer produces a valid pointer, but a -//! pointer pointing into previously allocated memory that since got freed is -//! not valid. The recommended way to build a Box to a ZST if `Box::new` cannot -//! be used is to use [`ptr::NonNull::dangling`]. +//! For zero-sized values, the `Box` pointer has to be non-null and sufficiently aligned. The +//! recommended way to build a Box to a ZST if `Box::new` cannot be used is to use +//! [`ptr::NonNull::dangling`]. +//! +//! On top of these basic layout requirements, a `Box` must point to a valid value of `T`. //! //! So long as `T: Sized`, a `Box` is guaranteed to be represented //! as a single pointer and is also ABI-compatible with C pointers @@ -187,26 +185,26 @@ 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::pin::{Pin, PinCoerceUnsized}; 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; @@ -222,9 +220,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`. @@ -265,8 +260,6 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(new_uninit)] - /// /// let mut five = Box::::new_uninit(); /// /// let five = unsafe { @@ -279,7 +272,7 @@ impl Box { /// assert_eq!(*five, 5) /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_uninit", issue = "63291")] + #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use] #[inline] pub fn new_uninit() -> Box> { @@ -295,7 +288,7 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(new_uninit)] + /// #![feature(new_zeroed_alloc)] /// /// let zero = Box::::new_zeroed(); /// let zero = unsafe { zero.assume_init() }; @@ -306,7 +299,7 @@ impl Box { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_uninit", issue = "63291")] + #[unstable(feature = "new_zeroed_alloc", issue = "129396")] #[must_use] pub fn new_zeroed() -> Box> { Self::new_zeroed_in(Global) @@ -352,7 +345,7 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// /// let mut five = Box::::try_new_uninit()?; /// @@ -382,7 +375,7 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// /// let zero = Box::::try_new_zeroed()?; /// let zero = unsafe { zero.assume_init() }; @@ -462,7 +455,7 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// /// use std::alloc::System; /// @@ -500,7 +493,7 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// /// use std::alloc::System; /// @@ -540,7 +533,7 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// /// use std::alloc::System; /// @@ -578,7 +571,7 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// /// use std::alloc::System; /// @@ -656,8 +649,6 @@ impl Box<[T]> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] - /// /// let mut values = Box::<[u32]>::new_uninit_slice(3); /// /// let values = unsafe { @@ -672,7 +663,7 @@ impl Box<[T]> { /// assert_eq!(*values, [1, 2, 3]) /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_uninit", issue = "63291")] + #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use] pub fn new_uninit_slice(len: usize) -> Box<[mem::MaybeUninit]> { unsafe { RawVec::with_capacity(len).into_box(len) } @@ -687,7 +678,7 @@ impl Box<[T]> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] + /// #![feature(new_zeroed_alloc)] /// /// let values = Box::<[u32]>::new_zeroed_slice(3); /// let values = unsafe { values.assume_init() }; @@ -697,19 +688,19 @@ impl Box<[T]> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_uninit", issue = "63291")] + #[unstable(feature = "new_zeroed_alloc", issue = "129396")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Box<[mem::MaybeUninit]> { unsafe { RawVec::with_capacity_zeroed(len).into_box(len) } } /// Constructs a new boxed slice with uninitialized contents. Returns an error if - /// the allocation fails + /// the allocation fails. /// /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// /// let mut values = Box::<[u32]>::try_new_uninit_slice(3)?; /// let values = unsafe { @@ -739,7 +730,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. @@ -747,7 +738,7 @@ impl Box<[T]> { /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// /// let values = Box::<[u32]>::try_new_zeroed_slice(3)?; /// let values = unsafe { values.assume_init() }; @@ -779,7 +770,7 @@ impl Box<[T], A> { /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// /// use std::alloc::System; /// @@ -813,7 +804,7 @@ impl Box<[T], A> { /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// /// use std::alloc::System; /// @@ -831,6 +822,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)] + /// + /// 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)] + /// + /// 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> { @@ -849,8 +919,6 @@ impl Box, A> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] - /// /// let mut five = Box::::new_uninit(); /// /// let five: Box = unsafe { @@ -862,7 +930,7 @@ impl Box, A> { /// /// assert_eq!(*five, 5) /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] + #[stable(feature = "new_uninit", since = "1.82.0")] #[inline] pub unsafe fn assume_init(self) -> Box { let (raw, alloc) = Box::into_raw_with_allocator(self); @@ -879,7 +947,7 @@ impl Box, A> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] + /// #![feature(box_uninit_write)] /// /// let big_box = Box::<[usize; 1024]>::new_uninit(); /// @@ -896,7 +964,7 @@ impl Box, A> { /// assert_eq!(*x, i); /// } /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] + #[unstable(feature = "box_uninit_write", issue = "129397")] #[inline] pub fn write(mut boxed: Self, value: T) -> Box { unsafe { @@ -922,8 +990,6 @@ impl Box<[mem::MaybeUninit], A> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] - /// /// let mut values = Box::<[u32]>::new_uninit_slice(3); /// /// let values = unsafe { @@ -937,7 +1003,7 @@ impl Box<[mem::MaybeUninit], A> { /// /// assert_eq!(*values, [1, 2, 3]) /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] + #[stable(feature = "new_uninit", since = "1.82.0")] #[inline] pub unsafe fn assume_init(self) -> Box<[T], A> { let (raw, alloc) = Box::into_raw_with_allocator(self); @@ -1097,6 +1163,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 { @@ -1150,6 +1217,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) { @@ -1176,6 +1244,95 @@ impl Box { unsafe { (Unique::from(&mut *ptr), alloc) } } + /// Returns a raw mutable pointer to the `Box`'s contents. + /// + /// The caller must ensure that the `Box` outlives the pointer this + /// function returns, or else it will end up dangling. + /// + /// This method guarantees that for the purpose of the aliasing model, this method + /// does not materialize a reference to the underlying memory, and thus the returned pointer + /// will remain valid when mixed with other calls to [`as_ptr`] and [`as_mut_ptr`]. + /// Note that calling other methods that materialize references to the memory + /// may still invalidate this pointer. + /// See the example below for how this guarantee can be used. + /// + /// # Examples + /// + /// Due to the aliasing guarantee, the following code is legal: + /// + /// ```rust + /// #![feature(box_as_ptr)] + /// + /// unsafe { + /// let mut b = Box::new(0); + /// let ptr1 = Box::as_mut_ptr(&mut b); + /// ptr1.write(1); + /// let ptr2 = Box::as_mut_ptr(&mut b); + /// ptr2.write(2); + /// // Notably, the write to `ptr2` did *not* invalidate `ptr1`: + /// ptr1.write(3); + /// } + /// ``` + /// + /// [`as_mut_ptr`]: Self::as_mut_ptr + /// [`as_ptr`]: Self::as_ptr + #[unstable(feature = "box_as_ptr", issue = "129090")] + #[rustc_never_returns_null_ptr] + #[inline] + pub fn as_mut_ptr(b: &mut Self) -> *mut T { + // This is a primitive deref, not going through `DerefMut`, and therefore not materializing + // any references. + ptr::addr_of_mut!(**b) + } + + /// Returns a raw pointer to the `Box`'s contents. + /// + /// The caller must ensure that the `Box` 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 contents of the `Box`, use [`as_mut_ptr`]. + /// + /// This method guarantees that for the purpose of the aliasing model, this method + /// does not materialize a reference to the underlying memory, and thus the returned pointer + /// will remain valid when mixed with other calls to [`as_ptr`] and [`as_mut_ptr`]. + /// Note that calling other methods that materialize mutable references to the memory, + /// as well as writing to this memory, may still invalidate this pointer. + /// See the example below for how this guarantee can be used. + /// + /// # Examples + /// + /// Due to the aliasing guarantee, the following code is legal: + /// + /// ```rust + /// #![feature(box_as_ptr)] + /// + /// unsafe { + /// let mut v = Box::new(0); + /// let ptr1 = Box::as_ptr(&v); + /// let ptr2 = Box::as_mut_ptr(&mut v); + /// let _val = ptr2.read(); + /// // No write to this memory has happened yet, so `ptr1` is still valid. + /// let _val = ptr1.read(); + /// // However, once we do a write... + /// ptr2.write(1); + /// // ... `ptr1` is no longer valid. + /// // This would be UB: let _val = ptr1.read(); + /// } + /// ``` + /// + /// [`as_mut_ptr`]: Self::as_mut_ptr + /// [`as_ptr`]: Self::as_ptr + #[unstable(feature = "box_as_ptr", issue = "129090")] + #[rustc_never_returns_null_ptr] + #[inline] + pub fn as_ptr(b: &Self) -> *const T { + // This is a primitive deref, not going through `DerefMut`, and therefore not materializing + // any references. + ptr::addr_of!(**b) + } + /// Returns a reference to the underlying allocator. /// /// Note: this is an associated function, which means that you have @@ -1189,9 +1346,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 @@ -1774,7 +1933,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 /// @@ -1833,7 +1992,7 @@ impl Box { } impl Box { - /// Attempt to downcast the box to a concrete type. + /// Attempts to downcast the box to a concrete type. /// /// # Examples /// @@ -1892,7 +2051,7 @@ impl Box { } impl Box { - /// Attempt to downcast the box to a concrete type. + /// Attempts to downcast the box to a concrete type. /// /// # Examples /// @@ -2646,3 +2805,6 @@ impl core::error::Error for Box { core::error::Error::provide(&**self, request); } } + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Box {} diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index e9bfecba160a0..9baded3a52141 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -2,7 +2,6 @@ //! //! by matthieu-m -use crate::alloc::{self, Layout, LayoutError}; use core::error::Error; use core::fmt::{self, Debug, Display, Formatter}; #[cfg(not(no_global_oom_handling))] @@ -14,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 af01db19139e3..fe9f1010d327c 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; @@ -484,7 +483,7 @@ impl BinaryHeap { /// heap.push(4); /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - #[rustc_const_unstable(feature = "const_binary_heap_new_in", 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 @@ -1361,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] @@ -1433,6 +1433,20 @@ pub struct Iter<'a, T: 'a> { iter: slice::Iter<'a, T>, } +#[stable(feature = "default_iters_sequel", since = "1.82.0")] +impl Default for Iter<'_, T> { + /// Creates an empty `binary_heap::Iter`. + /// + /// ``` + /// # use std::collections::binary_heap; + /// let iter: binary_heap::Iter<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + Iter { iter: Default::default() } + } +} + #[stable(feature = "collection_debug", since = "1.17.0")] impl fmt::Debug for Iter<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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..60e08b47e3d35 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. @@ -2016,6 +2016,20 @@ impl Default for Range<'_, K, V> { } } +#[stable(feature = "default_iters_sequel", since = "1.82.0")] +impl Default for RangeMut<'_, K, V> { + /// Creates an empty `btree_map::RangeMut`. + /// + /// ``` + /// # use std::collections::btree_map; + /// let iter: btree_map::RangeMut<'_, u8, u8> = Default::default(); + /// assert_eq!(iter.count(), 0); + /// ``` + fn default() -> Self { + RangeMut { inner: Default::default(), _marker: PhantomData } + } +} + #[stable(feature = "map_values_mut", since = "1.10.0")] impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { type Item = &'a mut V; @@ -2050,6 +2064,20 @@ impl ExactSizeIterator for ValuesMut<'_, K, V> { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ValuesMut<'_, K, V> {} +#[stable(feature = "default_iters_sequel", since = "1.82.0")] +impl Default for ValuesMut<'_, K, V> { + /// Creates an empty `btree_map::ValuesMut`. + /// + /// ``` + /// # use std::collections::btree_map; + /// let iter: btree_map::ValuesMut<'_, u8, u8> = Default::default(); + /// assert_eq!(iter.count(), 0); + /// ``` + fn default() -> Self { + ValuesMut { inner: Default::default() } + } +} + #[stable(feature = "map_into_keys_values", since = "1.54.0")] impl Iterator for IntoKeys { type Item = K; @@ -2921,7 +2949,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 +2991,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 +3089,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()?; @@ -3274,7 +3302,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> CursorMutKey<'a, K, V, A> { Some(kv) } - /// Removes the precending element from the `BTreeMap`. + /// Removes the preceding element from the `BTreeMap`. /// /// The element that was removed is returned. The cursor position is /// unchanged (after the removed element). @@ -3380,7 +3408,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> CursorMut<'a, K, V, A> { self.inner.remove_next() } - /// Removes the precending element from the `BTreeMap`. + /// Removes the preceding element from the `BTreeMap`. /// /// The element that was removed is returned. The cursor position is /// unchanged (after the removed element). 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 ba1f38dcc3e52..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. 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/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index d230749d71231..4d2fa0f094171 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -90,7 +90,7 @@ fn test_partial_eq() { #[test] #[cfg(target_arch = "x86_64")] -#[cfg_attr(miri, ignore)] // We'd like to run Miri with layout randomization +#[cfg_attr(any(miri, randomized_layouts), ignore)] // We'd like to run Miri with layout randomization fn test_sizes() { assert_eq!(core::mem::size_of::>(), 16); assert_eq!(core::mem::size_of::>(), 16 + CAPACITY * 2 * 8); 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..2b5bebcd8cd08 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 preceding 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 preceding 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 48bf767413835..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() { 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 077483a174b10..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}; diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index d3744c5a9d0c9..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(); @@ -1167,9 +1167,7 @@ fn test_drop_panic() { #[test] fn test_allocator() { - use core::alloc::AllocError; - use core::alloc::Allocator; - use core::alloc::Layout; + use core::alloc::{AllocError, Allocator, Layout}; use core::cell::Cell; struct A { 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 4747517393c66..2b09a5e7ddc58 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`. /// @@ -120,7 +121,6 @@ impl Iterator for IntoIter { { match self.try_fold(init, |b, item| Ok::(f(b, item))) { Ok(b) => b, - Err(e) => match e {}, } } @@ -241,7 +241,6 @@ impl DoubleEndedIterator for IntoIter { { match self.try_rfold(init, |b, item| Ok::(f(b, item))) { Ok(b) => b, - Err(e) => match e {}, } } } diff --git a/library/alloc/src/collections/vec_deque/iter.rs b/library/alloc/src/collections/vec_deque/iter.rs index 5a5e7f70854d8..bf4dd66f47638 100644 --- a/library/alloc/src/collections/vec_deque/iter.rs +++ b/library/alloc/src/collections/vec_deque/iter.rs @@ -28,6 +28,20 @@ impl fmt::Debug for Iter<'_, T> { } } +#[stable(feature = "default_iters_sequel", since = "1.82.0")] +impl Default for Iter<'_, T> { + /// Creates an empty `vec_deque::Iter`. + /// + /// ``` + /// # use std::collections::vec_deque; + /// let iter: vec_deque::Iter<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + Iter { i1: Default::default(), i2: Default::default() } + } +} + // FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Iter<'_, T> { diff --git a/library/alloc/src/collections/vec_deque/iter_mut.rs b/library/alloc/src/collections/vec_deque/iter_mut.rs index 5061931afb7b7..7a349a1b4edd0 100644 --- a/library/alloc/src/collections/vec_deque/iter_mut.rs +++ b/library/alloc/src/collections/vec_deque/iter_mut.rs @@ -28,6 +28,20 @@ impl fmt::Debug for IterMut<'_, T> { } } +#[stable(feature = "default_iters_sequel", since = "1.82.0")] +impl Default for IterMut<'_, T> { + /// Creates an empty `vec_deque::IterMut`. + /// + /// ``` + /// # use std::collections::vec_deque; + /// let iter: vec_deque::IterMut<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + IterMut { i1: Default::default(), i2: Default::default() } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> Iterator for IterMut<'a, T> { type Item = &'a mut T; diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index a07f250d7d88c..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; diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs index 6a89abc3ef9b6..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 { diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index f1eb195b88462..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. 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 c6bba619ae646..571fcd177aae7 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -581,7 +581,7 @@ pub use core::fmt::Alignment; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::Error; #[unstable(feature = "debug_closure_helpers", issue = "117729")] -pub use core::fmt::FormatterFn; +pub use core::fmt::{from_fn, FromFn}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{write, Arguments}; #[stable(feature = "rust1", since = "1.0.0")] @@ -600,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 a7715740cbd8f..7aaa4e73df72c 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -86,14 +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)] #![feature(array_chunks)] @@ -101,6 +100,7 @@ #![feature(array_windows)] #![feature(ascii_char)] #![feature(assert_matches)] +#![feature(async_closure)] #![feature(async_fn_traits)] #![feature(async_iterator)] #![feature(clone_to_uninit)] @@ -116,7 +116,6 @@ #![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)] @@ -131,13 +130,13 @@ #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] -#![feature(iter_repeat_n)] #![feature(layout_for_ptr)] #![feature(local_waker)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array_transpose)] #![feature(panic_internals)] #![feature(pattern)] +#![feature(pin_coerce_unsized_trait)] #![feature(ptr_internals)] #![feature(ptr_metadata)] #![feature(ptr_sub_ptr)] @@ -165,7 +164,6 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(c_unwind))] #![cfg_attr(not(test), feature(coroutine_trait))] #![cfg_attr(test, feature(panic_update_hook))] #![cfg_attr(test, feature(test))] @@ -185,6 +183,7 @@ #![feature(multiple_supertrait_upcastable)] #![feature(negative_impls)] #![feature(never_type)] +#![feature(optimize_attribute)] #![feature(rustc_allow_const_fn_unstable)] #![feature(rustc_attrs)] #![feature(slice_internals)] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 7b7dae5a057f0..a651ba067e47c 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::marker::PhantomData; +use core::mem::{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; @@ -41,6 +40,13 @@ struct Cap(usize); impl Cap { const ZERO: Cap = unsafe { Cap(0) }; + + /// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. + /// + /// # Safety: cap must be <= `isize::MAX`. + unsafe fn new(cap: usize) -> Self { + if T::IS_ZST { Cap::ZERO } else { unsafe { Self(cap) } } + } } /// A low-level utility for more ergonomically allocating, reallocating, and deallocating @@ -52,7 +58,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. @@ -67,7 +73,19 @@ impl Cap { /// `Box<[T]>`, since `capacity()` won't yield the length. #[allow(missing_debug_implementations)] pub(crate) struct RawVec { - ptr: Unique, + inner: RawVecInner, + _marker: PhantomData, +} + +/// Like a `RawVec`, but only generic over the allocator, not the type. +/// +/// As such, all the methods need the layout passed-in as a parameter. +/// +/// Having this separation reduces the amount of code we need to monomorphize, +/// as most operations don't need the actual type, just its layout. +#[allow(missing_debug_implementations)] +struct RawVecInner { + ptr: Unique, /// Never used for ZSTs; it's `capacity()`'s responsibility to return usize::MAX in that case. /// /// # Safety @@ -91,8 +109,9 @@ impl RawVec { /// `RawVec` with capacity `usize::MAX`. Useful for implementing /// delayed allocation. #[must_use] + #[rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81")] pub const fn new() -> Self { - Self::new_in(Global) + Self { inner: RawVecInner::new::(), _marker: PhantomData } } /// Creates a `RawVec` (on the system heap) with exactly the @@ -114,10 +133,7 @@ impl RawVec { #[must_use] #[inline] pub fn with_capacity(capacity: usize) -> Self { - match Self::try_allocate_in(capacity, AllocInit::Uninitialized, Global) { - Ok(res) => res, - Err(err) => handle_error(err), - } + Self { inner: RawVecInner::with_capacity(capacity, T::LAYOUT), _marker: PhantomData } } /// Like `with_capacity`, but guarantees the buffer is zeroed. @@ -125,29 +141,56 @@ impl RawVec { #[must_use] #[inline] pub fn with_capacity_zeroed(capacity: usize) -> Self { - Self::with_capacity_zeroed_in(capacity, Global) + Self { + inner: RawVecInner::with_capacity_zeroed_in(capacity, Global, T::LAYOUT), + _marker: PhantomData, + } } } -impl RawVec { - // Tiny Vecs are dumb. Skip to: - // - 8 if the element size is 1, because any heap allocators is likely - // to round up a request of less than 8 bytes to at least 8 bytes. - // - 4 if elements are moderate-sized (<= 1 KiB). - // - 1 otherwise, to avoid wasting too much space for very short Vecs. - pub(crate) const MIN_NON_ZERO_CAP: usize = if mem::size_of::() == 1 { +impl RawVecInner { + #[must_use] + #[rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81")] + const fn new() -> Self { + Self::new_in(Global, core::mem::align_of::()) + } + + #[cfg(not(any(no_global_oom_handling, test)))] + #[must_use] + #[inline] + fn with_capacity(capacity: usize, elem_layout: Layout) -> Self { + match Self::try_allocate_in(capacity, AllocInit::Uninitialized, Global, elem_layout) { + Ok(res) => res, + Err(err) => handle_error(err), + } + } +} + +// Tiny Vecs are dumb. Skip to: +// - 8 if the element size is 1, because any heap allocators is likely +// to round up a request of less than 8 bytes to at least 8 bytes. +// - 4 if elements are moderate-sized (<= 1 KiB). +// - 1 otherwise, to avoid wasting too much space for very short Vecs. +const fn min_non_zero_cap(size: usize) -> usize { + if size == 1 { 8 - } else if mem::size_of::() <= 1024 { + } else if size <= 1024 { 4 } else { 1 - }; + } +} + +impl RawVec { + #[cfg(not(no_global_oom_handling))] + pub(crate) const MIN_NON_ZERO_CAP: usize = min_non_zero_cap(size_of::()); /// Like `new`, but parameterized over the choice of allocator for /// the returned `RawVec`. + #[inline] + #[rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81")] pub const fn new_in(alloc: A) -> Self { - // `cap: 0` means "unallocated". zero-sized types are ignored. - Self { ptr: Unique::dangling(), cap: Cap::ZERO, alloc } + Self { inner: RawVecInner::new_in(alloc, align_of::()), _marker: PhantomData } } /// Like `with_capacity`, but parameterized over the choice of @@ -155,9 +198,9 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { - match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc) { - Ok(res) => res, - Err(err) => handle_error(err), + Self { + inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), + _marker: PhantomData, } } @@ -165,7 +208,10 @@ impl RawVec { /// allocator for the returned `RawVec`. #[inline] pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { - Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc) + match RawVecInner::try_with_capacity_in(capacity, alloc, T::LAYOUT) { + Ok(inner) => Ok(Self { inner, _marker: PhantomData }), + Err(e) => Err(e), + } } /// Like `with_capacity_zeroed`, but parameterized over the choice @@ -173,9 +219,9 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { - match Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc) { - Ok(res) => res, - Err(err) => handle_error(err), + Self { + inner: RawVecInner::with_capacity_zeroed_in(capacity, alloc, T::LAYOUT), + _marker: PhantomData, } } @@ -201,45 +247,7 @@ impl RawVec { let me = ManuallyDrop::new(self); unsafe { let slice = ptr::slice_from_raw_parts_mut(me.ptr() as *mut MaybeUninit, len); - Box::from_raw_in(slice, ptr::read(&me.alloc)) - } - } - - fn try_allocate_in( - capacity: usize, - init: AllocInit, - alloc: A, - ) -> Result { - // Don't allocate here because `Drop` will not deallocate when `capacity` is 0. - - if T::IS_ZST || capacity == 0 { - Ok(Self::new_in(alloc)) - } else { - // We avoid `unwrap_or_else` here because it bloats the amount of - // LLVM IR generated. - let layout = match Layout::array::(capacity) { - Ok(layout) => layout, - Err(_) => return Err(CapacityOverflow.into()), - }; - - if let Err(err) = alloc_guard(layout.size()) { - return Err(err); - } - - let result = match init { - AllocInit::Uninitialized => alloc.allocate(layout), - #[cfg(not(no_global_oom_handling))] - AllocInit::Zeroed => alloc.allocate_zeroed(layout), - }; - let ptr = match result { - Ok(ptr) => ptr, - Err(_) => return Err(AllocError { layout, non_exhaustive: () }.into()), - }; - - // Allocators currently return a `NonNull<[u8]>` whose length - // matches the size requested. If that ever changes, the capacity - // here should change to `ptr.len() / mem::size_of::()`. - Ok(Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc }) + Box::from_raw_in(slice, ptr::read(&me.inner.alloc)) } } @@ -255,8 +263,15 @@ impl RawVec { /// guaranteed. #[inline] pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { - let cap = if T::IS_ZST { Cap::ZERO } else { unsafe { Cap(capacity) } }; - Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap, alloc } + // SAFETY: Precondition passed to the caller + unsafe { + let ptr = ptr.cast(); + let capacity = Cap::new::(capacity); + Self { + inner: RawVecInner::from_raw_parts_in(ptr, capacity, alloc), + _marker: PhantomData, + } + } } /// A convenience method for hoisting the non-null precondition out of [`RawVec::from_raw_parts_in`]. @@ -265,9 +280,13 @@ impl RawVec { /// /// See [`RawVec::from_raw_parts_in`]. #[inline] - pub(crate) unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { - let cap = if T::IS_ZST { Cap::ZERO } else { unsafe { Cap(capacity) } }; - Self { ptr: Unique::from(ptr), cap, alloc } + pub unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { + // SAFETY: Precondition passed to the caller + unsafe { + let ptr = ptr.cast(); + let capacity = Cap::new::(capacity); + Self { inner: RawVecInner::from_nonnull_in(ptr, capacity, alloc), _marker: PhantomData } + } } /// Gets a raw pointer to the start of the allocation. Note that this is @@ -275,43 +294,26 @@ impl RawVec { /// be careful. #[inline] pub fn ptr(&self) -> *mut T { - self.ptr.as_ptr() + self.inner.ptr() } #[inline] pub fn non_null(&self) -> NonNull { - NonNull::from(self.ptr) + self.inner.non_null() } /// Gets the capacity of the allocation. /// /// This will always be `usize::MAX` if `T` is zero-sized. - #[inline(always)] + #[inline] pub fn capacity(&self) -> usize { - if T::IS_ZST { usize::MAX } else { self.cap.0 } + self.inner.capacity(size_of::()) } /// Returns a shared reference to the allocator backing this `RawVec`. + #[inline] pub fn allocator(&self) -> &A { - &self.alloc - } - - fn current_memory(&self) -> Option<(NonNull, Layout)> { - if T::IS_ZST || self.cap.0 == 0 { - None - } else { - // We could use Layout::array here which ensures the absence of isize and usize overflows - // and could hypothetically handle differences between stride and size, but this memory - // has already been allocated so we know it can't overflow and currently Rust does not - // support such types. So we can do better by skipping some checks and avoid an unwrap. - const { assert!(mem::size_of::() % mem::align_of::() == 0) }; - unsafe { - let align = mem::align_of::(); - let size = mem::size_of::().unchecked_mul(self.cap.0); - let layout = Layout::from_size_align_unchecked(size, align); - Some((self.ptr.cast().into(), layout)) - } - } + self.inner.allocator() } /// Ensures that the buffer contains at least enough space to hold `len + @@ -336,24 +338,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn reserve(&mut self, len: usize, additional: usize) { - // Callers expect this function to be very cheap when there is already sufficient capacity. - // Therefore, we move all the resizing and error-handling logic from grow_amortized and - // handle_reserve behind a call, while making sure that this function is likely to be - // inlined as just a comparison and a call if the comparison fails. - #[cold] - fn do_reserve_and_handle( - slf: &mut RawVec, - len: usize, - additional: usize, - ) { - if let Err(err) = slf.grow_amortized(len, additional) { - handle_error(err); - } - } - - if self.needs_to_grow(len, additional) { - do_reserve_and_handle(self, len, additional); - } + self.inner.reserve(len, additional, T::LAYOUT) } /// A specialized version of `self.reserve(len, 1)` which requires the @@ -361,21 +346,12 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline(never)] pub fn grow_one(&mut self) { - if let Err(err) = self.grow_amortized(self.cap.0, 1) { - handle_error(err); - } + self.inner.grow_one(T::LAYOUT) } /// The same as `reserve`, but returns on errors instead of panicking or aborting. pub fn try_reserve(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { - if self.needs_to_grow(len, additional) { - self.grow_amortized(len, additional)?; - } - unsafe { - // Inform the optimizer that the reservation has succeeded or wasn't needed - hint::assert_unchecked(!self.needs_to_grow(len, additional)); - } - Ok(()) + self.inner.try_reserve(len, additional, T::LAYOUT) } /// Ensures that the buffer contains at least enough space to hold `len + @@ -397,9 +373,7 @@ impl RawVec { /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] pub fn reserve_exact(&mut self, len: usize, additional: usize) { - if let Err(err) = self.try_reserve_exact(len, additional) { - handle_error(err); - } + self.inner.reserve_exact(len, additional, T::LAYOUT) } /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. @@ -408,14 +382,7 @@ impl RawVec { len: usize, additional: usize, ) -> Result<(), TryReserveError> { - if self.needs_to_grow(len, additional) { - self.grow_exact(len, additional)?; - } - unsafe { - // Inform the optimizer that the reservation has succeeded or wasn't needed - hint::assert_unchecked(!self.needs_to_grow(len, additional)); - } - Ok(()) + self.inner.try_reserve_exact(len, additional, T::LAYOUT) } /// Shrinks the buffer down to the specified capacity. If the given amount @@ -431,22 +398,230 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn shrink_to_fit(&mut self, cap: usize) { - if let Err(err) = self.shrink(cap) { + self.inner.shrink_to_fit(cap, T::LAYOUT) + } +} + +unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { + /// Frees the memory owned by the `RawVec` *without* trying to drop its contents. + fn drop(&mut self) { + // SAFETY: We are in a Drop impl, self.inner will not be used again. + unsafe { self.inner.deallocate(T::LAYOUT) } + } +} + +impl RawVecInner { + #[inline] + #[rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81")] + const fn new_in(alloc: A, align: usize) -> Self { + let ptr = unsafe { core::mem::transmute(align) }; + // `cap: 0` means "unallocated". zero-sized types are ignored. + Self { ptr, cap: Cap::ZERO, alloc } + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + fn with_capacity_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { + match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) { + Ok(this) => { + unsafe { + // Make it more obvious that a subsquent Vec::reserve(capacity) will not allocate. + hint::assert_unchecked(!this.needs_to_grow(0, capacity, elem_layout)); + } + this + } + Err(err) => handle_error(err), + } + } + + #[inline] + fn try_with_capacity_in( + capacity: usize, + alloc: A, + elem_layout: Layout, + ) -> Result { + Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + fn with_capacity_zeroed_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { + match Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc, elem_layout) { + Ok(res) => res, + Err(err) => handle_error(err), + } + } + + fn try_allocate_in( + capacity: usize, + init: AllocInit, + alloc: A, + elem_layout: Layout, + ) -> Result { + // We avoid `unwrap_or_else` here because it bloats the amount of + // LLVM IR generated. + let layout = match layout_array(capacity, elem_layout) { + Ok(layout) => layout, + Err(_) => return Err(CapacityOverflow.into()), + }; + + // Don't allocate here because `Drop` will not deallocate when `capacity` is 0. + if layout.size() == 0 { + return Ok(Self::new_in(alloc, elem_layout.align())); + } + + if let Err(err) = alloc_guard(layout.size()) { + return Err(err); + } + + let result = match init { + AllocInit::Uninitialized => alloc.allocate(layout), + #[cfg(not(no_global_oom_handling))] + AllocInit::Zeroed => alloc.allocate_zeroed(layout), + }; + let ptr = match result { + Ok(ptr) => ptr, + Err(_) => return Err(AllocError { layout, non_exhaustive: () }.into()), + }; + + // Allocators currently return a `NonNull<[u8]>` whose length + // matches the size requested. If that ever changes, the capacity + // here should change to `ptr.len() / mem::size_of::()`. + Ok(Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc }) + } + + #[inline] + unsafe fn from_raw_parts_in(ptr: *mut u8, cap: Cap, alloc: A) -> Self { + Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap, alloc } + } + + #[inline] + unsafe fn from_nonnull_in(ptr: NonNull, cap: Cap, alloc: A) -> Self { + Self { ptr: Unique::from(ptr), cap, alloc } + } + + #[inline] + fn ptr(&self) -> *mut T { + self.non_null::().as_ptr() + } + + #[inline] + fn non_null(&self) -> NonNull { + self.ptr.cast().into() + } + + #[inline] + fn capacity(&self, elem_size: usize) -> usize { + if elem_size == 0 { usize::MAX } else { self.cap.0 } + } + + #[inline] + fn allocator(&self) -> &A { + &self.alloc + } + + #[inline] + fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull, Layout)> { + if elem_layout.size() == 0 || self.cap.0 == 0 { + None + } else { + // We could use Layout::array here which ensures the absence of isize and usize overflows + // and could hypothetically handle differences between stride and size, but this memory + // has already been allocated so we know it can't overflow and currently Rust does not + // support such types. So we can do better by skipping some checks and avoid an unwrap. + unsafe { + let alloc_size = elem_layout.size().unchecked_mul(self.cap.0); + let layout = Layout::from_size_align_unchecked(alloc_size, elem_layout.align()); + Some((self.ptr.into(), layout)) + } + } + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) { + // Callers expect this function to be very cheap when there is already sufficient capacity. + // Therefore, we move all the resizing and error-handling logic from grow_amortized and + // handle_reserve behind a call, while making sure that this function is likely to be + // inlined as just a comparison and a call if the comparison fails. + #[cold] + fn do_reserve_and_handle( + slf: &mut RawVecInner, + len: usize, + additional: usize, + elem_layout: Layout, + ) { + if let Err(err) = slf.grow_amortized(len, additional, elem_layout) { + handle_error(err); + } + } + + if self.needs_to_grow(len, additional, elem_layout) { + do_reserve_and_handle(self, len, additional, elem_layout); + } + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + fn grow_one(&mut self, elem_layout: Layout) { + if let Err(err) = self.grow_amortized(self.cap.0, 1, elem_layout) { handle_error(err); } } -} -impl RawVec { - /// Returns if the buffer needs to grow to fulfill the needed extra capacity. - /// Mainly used to make inlining reserve-calls possible without inlining `grow`. - fn needs_to_grow(&self, len: usize, additional: usize) -> bool { - additional > self.capacity().wrapping_sub(len) + fn try_reserve( + &mut self, + len: usize, + additional: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + if self.needs_to_grow(len, additional, elem_layout) { + self.grow_amortized(len, additional, elem_layout)?; + } + unsafe { + // Inform the optimizer that the reservation has succeeded or wasn't needed + hint::assert_unchecked(!self.needs_to_grow(len, additional, elem_layout)); + } + Ok(()) } - /// # Safety: - /// - /// `cap` must not exceed `isize::MAX`. + #[cfg(not(no_global_oom_handling))] + fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) { + if let Err(err) = self.try_reserve_exact(len, additional, elem_layout) { + handle_error(err); + } + } + + fn try_reserve_exact( + &mut self, + len: usize, + additional: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + if self.needs_to_grow(len, additional, elem_layout) { + self.grow_exact(len, additional, elem_layout)?; + } + unsafe { + // Inform the optimizer that the reservation has succeeded or wasn't needed + hint::assert_unchecked(!self.needs_to_grow(len, additional, elem_layout)); + } + Ok(()) + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) { + if let Err(err) = self.shrink(cap, elem_layout) { + handle_error(err); + } + } + + #[inline] + fn needs_to_grow(&self, len: usize, additional: usize, elem_layout: Layout) -> bool { + additional > self.capacity(elem_layout.size()).wrapping_sub(len) + } + + #[inline] unsafe fn set_ptr_and_cap(&mut self, ptr: NonNull<[u8]>, cap: usize) { // Allocators currently return a `NonNull<[u8]>` whose length matches // the size requested. If that ever changes, the capacity here should @@ -455,18 +630,16 @@ impl RawVec { self.cap = unsafe { Cap(cap) }; } - // This method is usually instantiated many times. So we want it to be as - // small as possible, to improve compile times. But we also want as much of - // its contents to be statically computable as possible, to make the - // generated code run faster. Therefore, this method is carefully written - // so that all of the code that depends on `T` is within it, while as much - // of the code that doesn't depend on `T` as possible is in functions that - // are non-generic over `T`. - fn grow_amortized(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { + fn grow_amortized( + &mut self, + len: usize, + additional: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { // This is ensured by the calling contexts. debug_assert!(additional > 0); - if T::IS_ZST { + if elem_layout.size() == 0 { // Since we return a capacity of `usize::MAX` when `elem_size` is // 0, getting to here necessarily means the `RawVec` is overfull. return Err(CapacityOverflow.into()); @@ -478,33 +651,34 @@ impl RawVec { // This guarantees exponential growth. The doubling cannot overflow // because `cap <= isize::MAX` and the type of `cap` is `usize`. let cap = cmp::max(self.cap.0 * 2, required_cap); - let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap); + let cap = cmp::max(min_non_zero_cap(elem_layout.size()), cap); + + let new_layout = layout_array(cap, elem_layout)?; - let new_layout = Layout::array::(cap); + let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?; + // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items - // `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 unsafe { self.set_ptr_and_cap(ptr, cap) }; Ok(()) } - // The constraints on this method are much the same as those on - // `grow_amortized`, but this method is usually instantiated less often so - // it's less critical. - fn grow_exact(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { - if T::IS_ZST { + fn grow_exact( + &mut self, + len: usize, + additional: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + if elem_layout.size() == 0 { // Since we return a capacity of `usize::MAX` when the type size is // 0, getting to here necessarily means the `RawVec` is overfull. return Err(CapacityOverflow.into()); } let cap = len.checked_add(additional).ok_or(CapacityOverflow)?; - let new_layout = Layout::array::(cap); + let new_layout = layout_array(cap, elem_layout)?; - // `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 + let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?; + // 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); } @@ -513,10 +687,10 @@ 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"); + fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> { + assert!(cap <= self.capacity(elem_layout.size()), "Tried to shrink to a larger capacity"); // SAFETY: Just checked this isn't trying to grow - unsafe { self.shrink_unchecked(cap) } + unsafe { self.shrink_unchecked(cap, elem_layout) } } /// `shrink`, but without the capacity check. @@ -530,23 +704,27 @@ impl RawVec { /// # 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) }; + unsafe fn shrink_unchecked( + &mut self, + cap: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + let (ptr, layout) = + if let Some(mem) = self.current_memory(elem_layout) { mem } else { return Ok(()) }; // If shrinking to 0, deallocate the buffer. We don't reach this point // for the T::IS_ZST case since current_memory() will have returned // None. if cap == 0 { unsafe { self.alloc.deallocate(ptr, layout) }; - self.ptr = Unique::dangling(); + self.ptr = + unsafe { Unique::new_unchecked(ptr::without_provenance_mut(elem_layout.align())) }; self.cap = Cap::ZERO; } else { let ptr = unsafe { - // `Layout::array` cannot overflow here because it would have + // Layout cannot overflow here because it would have // overflowed earlier when capacity was larger. - let new_size = mem::size_of::().unchecked_mul(cap); + let new_size = elem_layout.size().unchecked_mul(cap); let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); self.alloc .shrink(ptr, layout, new_layout) @@ -559,24 +737,32 @@ impl RawVec { } Ok(()) } + + /// # Safety + /// + /// This function deallocates the owned allocation, but does not update `ptr` or `cap` to + /// prevent double-free or use-after-free. Essentially, do not do anything with the caller + /// after this function returns. + /// Ideally this function would take `self` by move, but it cannot because it exists to be + /// called from a `Drop` impl. + unsafe fn deallocate(&mut self, elem_layout: Layout) { + if let Some((ptr, layout)) = self.current_memory(elem_layout) { + unsafe { + self.alloc.deallocate(ptr, layout); + } + } + } } -// This function is outside `RawVec` to minimize compile times. See the comment -// above `RawVec::grow_amortized` for details. (The `A` parameter isn't -// significant, because the number of different `A` types seen in practice is -// much smaller than the number of `T` types.) #[inline(never)] fn finish_grow( - new_layout: Result, + new_layout: Layout, current_memory: Option<(NonNull, Layout)>, alloc: &mut A, ) -> Result, TryReserveError> where A: Allocator, { - // Check for the error here to minimize the size of `RawVec::grow_*`. - let new_layout = new_layout.map_err(|_| CapacityOverflow)?; - alloc_guard(new_layout.size())?; let memory = if let Some((ptr, old_layout)) = current_memory { @@ -593,18 +779,10 @@ where memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () }.into()) } -unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { - /// Frees the memory owned by the `RawVec` *without* trying to drop its contents. - fn drop(&mut self) { - if let Some((ptr, layout)) = self.current_memory() { - unsafe { self.alloc.deallocate(ptr, layout) } - } - } -} - // Central function for reserve error handling. #[cfg(not(no_global_oom_handling))] #[cold] +#[optimize(size)] fn handle_error(e: TryReserveError) -> ! { match e.kind() { CapacityOverflow => capacity_overflow(), @@ -628,3 +806,8 @@ fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> { Ok(()) } } + +#[inline] +fn layout_array(cap: usize, elem_layout: Layout) -> Result { + elem_layout.repeat(cap).map(|(layout, _pad)| layout).map_err(|_| CapacityOverflow.into()) +} diff --git a/library/alloc/src/raw_vec/tests.rs b/library/alloc/src/raw_vec/tests.rs index 4194be530612d..d78ded104fb09 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; @@ -42,9 +43,9 @@ fn allocator_param() { let a = BoundedAlloc { fuel: Cell::new(500) }; let mut v: RawVec = RawVec::with_capacity_in(50, a); - assert_eq!(v.alloc.fuel.get(), 450); + assert_eq!(v.inner.alloc.fuel.get(), 450); v.reserve(50, 150); // (causes a realloc, thus using 50 + 150 = 200 units of fuel) - assert_eq!(v.alloc.fuel.get(), 250); + assert_eq!(v.inner.alloc.fuel.get(), 250); } #[test] @@ -85,7 +86,7 @@ struct ZST; fn zst_sanity(v: &RawVec) { assert_eq!(v.capacity(), usize::MAX); assert_eq!(v.ptr(), core::ptr::Unique::::dangling().as_ptr()); - assert_eq!(v.current_memory(), None); + assert_eq!(v.inner.current_memory(T::LAYOUT), None); } #[test] @@ -105,22 +106,11 @@ fn zst() { let v: RawVec = RawVec::with_capacity_in(100, Global); zst_sanity(&v); - let v: RawVec = RawVec::try_allocate_in(0, AllocInit::Uninitialized, Global).unwrap(); - zst_sanity(&v); - - let v: RawVec = RawVec::try_allocate_in(100, AllocInit::Uninitialized, Global).unwrap(); - zst_sanity(&v); - - let mut v: RawVec = - RawVec::try_allocate_in(usize::MAX, AllocInit::Uninitialized, Global).unwrap(); + let mut v: RawVec = RawVec::with_capacity_in(usize::MAX, Global); zst_sanity(&v); // Check all these operations work as expected with zero-sized elements. - assert!(!v.needs_to_grow(100, usize::MAX - 100)); - assert!(v.needs_to_grow(101, usize::MAX - 100)); - zst_sanity(&v); - v.reserve(100, usize::MAX - 100); //v.reserve(101, usize::MAX - 100); // panics, in `zst_reserve_panic` below zst_sanity(&v); @@ -137,12 +127,12 @@ fn zst() { assert_eq!(v.try_reserve_exact(101, usize::MAX - 100), cap_err); zst_sanity(&v); - assert_eq!(v.grow_amortized(100, usize::MAX - 100), cap_err); - assert_eq!(v.grow_amortized(101, usize::MAX - 100), cap_err); + assert_eq!(v.inner.grow_amortized(100, usize::MAX - 100, ZST::LAYOUT), cap_err); + assert_eq!(v.inner.grow_amortized(101, usize::MAX - 100, ZST::LAYOUT), cap_err); zst_sanity(&v); - assert_eq!(v.grow_exact(100, usize::MAX - 100), cap_err); - assert_eq!(v.grow_exact(101, usize::MAX - 100), cap_err); + assert_eq!(v.inner.grow_exact(100, usize::MAX - 100, ZST::LAYOUT), cap_err); + assert_eq!(v.inner.grow_exact(101, usize::MAX - 100, ZST::LAYOUT), cap_err); zst_sanity(&v); } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 9982c8ea6dcbe..88c7a12db23ca 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -241,37 +241,35 @@ #![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}; -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))] use core::pin::Pin; +use core::pin::PinCoerceUnsized; 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; 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))] @@ -439,7 +437,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. @@ -449,7 +447,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() /// } @@ -505,7 +503,6 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(get_mut_unchecked)] /// /// use std::rc::Rc; @@ -520,7 +517,7 @@ impl Rc { /// assert_eq!(*five, 5) /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_uninit", issue = "63291")] + #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use] pub fn new_uninit() -> Rc> { unsafe { @@ -541,7 +538,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(new_uninit)] + /// #![feature(new_zeroed_alloc)] /// /// use std::rc::Rc; /// @@ -553,7 +550,7 @@ impl Rc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_uninit", issue = "63291")] + #[unstable(feature = "new_zeroed_alloc", issue = "129396")] #[must_use] pub fn new_zeroed() -> Rc> { unsafe { @@ -595,7 +592,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// #![feature(get_mut_unchecked)] /// /// use std::rc::Rc; @@ -631,7 +628,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// /// use std::rc::Rc; /// @@ -693,7 +690,6 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(get_mut_unchecked)] /// #![feature(allocator_api)] /// @@ -737,7 +733,6 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(allocator_api)] /// /// use std::rc::Rc; @@ -800,7 +795,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// #![feature(get_mut_unchecked)] /// /// use std::rc::Rc; @@ -844,7 +839,7 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(allocator_api, new_uninit)] + /// #![feature(allocator_api)] /// /// use std::rc::Rc; /// use std::alloc::System; @@ -908,19 +903,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) } @@ -969,7 +963,6 @@ impl Rc<[T]> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(get_mut_unchecked)] /// /// use std::rc::Rc; @@ -987,7 +980,7 @@ impl Rc<[T]> { /// assert_eq!(*values, [1, 2, 3]) /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_uninit", issue = "63291")] + #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use] pub fn new_uninit_slice(len: usize) -> Rc<[mem::MaybeUninit]> { unsafe { Rc::from_ptr(Rc::allocate_for_slice(len)) } @@ -1002,7 +995,7 @@ impl Rc<[T]> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] + /// #![feature(new_zeroed_alloc)] /// /// use std::rc::Rc; /// @@ -1014,7 +1007,7 @@ impl Rc<[T]> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_uninit", issue = "63291")] + #[unstable(feature = "new_zeroed_alloc", issue = "129396")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Rc<[mem::MaybeUninit]> { unsafe { @@ -1036,7 +1029,6 @@ impl Rc<[T], A> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(get_mut_unchecked)] /// #![feature(allocator_api)] /// @@ -1073,7 +1065,6 @@ impl Rc<[T], A> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(allocator_api)] /// /// use std::rc::Rc; @@ -1123,7 +1114,6 @@ impl Rc, A> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(get_mut_unchecked)] /// /// use std::rc::Rc; @@ -1137,7 +1127,7 @@ impl Rc, A> { /// /// assert_eq!(*five, 5) /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] + #[stable(feature = "new_uninit", since = "1.82.0")] #[inline] pub unsafe fn assume_init(self) -> Rc { let (ptr, alloc) = Rc::into_inner_with_allocator(self); @@ -1161,7 +1151,6 @@ impl Rc<[mem::MaybeUninit], A> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(get_mut_unchecked)] /// /// use std::rc::Rc; @@ -1178,7 +1167,7 @@ impl Rc<[mem::MaybeUninit], A> { /// /// assert_eq!(*values, [1, 2, 3]) /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] + #[stable(feature = "new_uninit", since = "1.82.0")] #[inline] pub unsafe fn assume_init(self) -> Rc<[T], A> { let (ptr, alloc) = Rc::into_inner_with_allocator(self); @@ -1354,9 +1343,8 @@ impl Rc { #[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. @@ -1377,6 +1365,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); @@ -1902,7 +1891,7 @@ impl Rc { } impl Rc { - /// Attempt to downcast the `Rc` to a concrete type. + /// Attempts to downcast the `Rc` to a concrete type. /// /// # Examples /// @@ -2127,7 +2116,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) } @@ -2181,6 +2170,12 @@ impl Deref for Rc { } } +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Rc {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Weak {} + #[unstable(feature = "deref_pure_trait", issue = "87121")] unsafe impl DerefPure for Rc {} @@ -2588,7 +2583,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 /// @@ -2607,7 +2602,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 /// @@ -2626,7 +2621,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 /// @@ -2664,7 +2659,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 /// @@ -2697,8 +2692,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 /// @@ -3080,9 +3075,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. @@ -3114,6 +3107,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) { @@ -3530,7 +3524,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 /// @@ -3696,6 +3690,9 @@ impl Deref for UniqueRc { } } +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for UniqueRc {} + #[unstable(feature = "unique_rc_arc", issue = "112566")] impl DerefMut for UniqueRc { fn deref_mut(&mut self) -> &mut T { @@ -3738,7 +3735,7 @@ struct UniqueRcUninit { #[cfg(not(no_global_oom_handling))] impl UniqueRcUninit { - /// Allocate a RcBox with layout suitable to contain `for_value` or a clone of it. + /// 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 { @@ -3762,10 +3759,11 @@ impl UniqueRcUninit { /// # Safety /// /// The data must have been initialized (by writing to [`Self::data_ptr()`]). - unsafe fn into_rc(mut self) -> Rc { - let ptr = self.ptr; - let alloc = self.alloc.take().unwrap(); - mem::forget(self); + 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) } diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs index 5e2e4beb94a2b..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)); diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index c7960b3fb49c3..9d70487032699 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -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)] @@ -179,15 +178,25 @@ impl [T] { /// 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. + /// If the implementation of [`Ord`] for `T` does not implement a [total order] the resulting + /// order of elements in the slice 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 implementation of [`Ord`] for `T` 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). The exception are partially sorted slices, which /// may be better served with `slice::sort`. /// + /// Sorting types that only implement [`PartialOrd`] such as [`f32`] and [`f64`] require + /// additional precautions. For example, `f32::NAN != f32::NAN`, which doesn't fulfill the + /// reflexivity requirement of [`Ord`]. By using an alternative comparison function with + /// `slice::sort_by` such as [`f32::total_cmp`] or [`f64::total_cmp`] that defines a [total + /// order] users can sort slices containing floating-point values. Alternatively, if all values + /// in the slice are guaranteed to be in a subset for which [`PartialOrd::partial_cmp`] forms a + /// [total order], it's possible to sort the slice with `sort_by(|a, b| + /// a.partial_cmp(b).unwrap())`. + /// /// # Current implementation /// /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which @@ -199,18 +208,21 @@ impl [T] { /// 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. + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [-5, 4, 1, -3, 2]; + /// let mut v = [4, -5, 1, -3, 2]; /// /// v.sort(); - /// assert!(v == [-5, -3, 1, 2, 4]); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); /// ``` /// /// [driftsort]: https://github.com/Voultapher/driftsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] @@ -222,30 +234,19 @@ impl [T] { stable_sort(self, T::lt); } - /// Sorts the slice with a comparator function, preserving initial order of equal elements. + /// Sorts the slice with a comparison 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. /// - /// 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 comparison function `compare` does not implement a [total order] the resulting order + /// of elements in the slice 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 `compare` panics. /// - /// 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 `>`. - /// - /// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use - /// `partial_cmp` as our sort function when we know the slice doesn't contain a `NaN`. - /// - /// ``` - /// let mut floats = [5f64, 4.0, 1.0, 3.0, 2.0]; - /// floats.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); - /// assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 5.0]); - /// ``` + /// For example `|a, b| (a - b).cmp(a)` is a comparison function that is neither transitive nor + /// reflexive nor total, `a < b < c < a` with `a = 1, b = 2, c = 3`. For more information and + /// examples see the [`Ord`] documentation. /// /// # Current implementation /// @@ -258,21 +259,24 @@ impl [T] { /// 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. + /// # Panics + /// + /// May panic if `compare` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [5, 4, 1, 3, 2]; + /// let mut v = [4, -5, 1, -3, 2]; /// v.sort_by(|a, b| a.cmp(b)); - /// assert!(v == [1, 2, 3, 4, 5]); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); /// /// // reverse sorting /// v.sort_by(|a, b| b.cmp(a)); - /// assert!(v == [5, 4, 3, 2, 1]); + /// assert_eq!(v, [4, 2, 1, -3, -5]); /// ``` /// /// [driftsort]: https://github.com/Voultapher/driftsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] @@ -289,9 +293,10 @@ impl [T] { /// 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*). /// - /// 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. + /// If the implementation of [`Ord`] for `K` does not implement a [total order] the resulting + /// order of elements in the slice 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 implementation of [`Ord`] for `K` panics. /// /// # Current implementation /// @@ -304,18 +309,21 @@ impl [T] { /// 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. + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [-5i32, 4, 1, -3, 2]; + /// let mut v = [4i32, -5, 1, -3, 2]; /// /// v.sort_by_key(|k| k.abs()); - /// assert!(v == [1, 2, -3, 4, -5]); + /// assert_eq!(v, [1, 2, -3, 4, -5]); /// ``` /// /// [driftsort]: https://github.com/Voultapher/driftsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "slice_sort_by_key", since = "1.7.0")] @@ -337,9 +345,10 @@ impl [T] { /// 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. + /// If the implementation of [`Ord`] for `K` does not implement a [total order] the resulting + /// order of elements in the slice 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 implementation of [`Ord`] for `K` 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. @@ -356,16 +365,22 @@ impl [T] { /// In the worst case, the algorithm allocates temporary storage in a `Vec<(K, usize)>` the /// length of the slice. /// + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order]. + /// /// # Examples /// /// ``` - /// let mut v = [-5i32, 4, 32, -3, 2]; + /// let mut v = [4i32, -5, 1, -3, 2, 10]; /// + /// // Strings are sorted by lexicographical order. /// v.sort_by_cached_key(|k| k.to_string()); - /// assert!(v == [-3, -5, 2, 32, 4]); + /// assert_eq!(v, [-3, -5, 1, 10, 2, 4]); /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "slice_sort_by_cached_key", since = "1.34.0")] diff --git a/library/alloc/src/slice/tests.rs b/library/alloc/src/slice/tests.rs index 0b972a13898eb..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(); diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 3bb808a6c73ab..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. @@ -269,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) { @@ -309,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 07ffd3e151914..bc8b7e24bf12b 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 = { @@ -2288,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<&'a str> + where + Self::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>, + { self[..].strip_suffix_of(haystack) } } @@ -2643,14 +2643,54 @@ impl ToString for i8 { } } -#[doc(hidden)] +// Generic/generated code can sometimes have multiple, nested references +// for strings, including `&&&str`s that would never be written +// by hand. This macro generates twelve layers of nested `&`-impl +// for primitive strings. #[cfg(not(no_global_oom_handling))] -#[stable(feature = "str_to_string_specialization", since = "1.9.0")] -impl ToString for str { - #[inline] - fn to_string(&self) -> String { - String::from(self) - } +macro_rules! to_string_str_wrap_in_ref { + {x $($x:ident)*} => { + &to_string_str_wrap_in_ref! { $($x)* } + }; + {} => { str }; +} +#[cfg(not(no_global_oom_handling))] +macro_rules! to_string_expr_wrap_in_deref { + {$self:expr ; x $($x:ident)*} => { + *(to_string_expr_wrap_in_deref! { $self ; $($x)* }) + }; + {$self:expr ;} => { $self }; +} +#[cfg(not(no_global_oom_handling))] +macro_rules! to_string_str { + {$($($x:ident)*),+} => { + $( + #[doc(hidden)] + #[stable(feature = "str_to_string_specialization", since = "1.9.0")] + impl ToString for to_string_str_wrap_in_ref!($($x)*) { + #[inline] + fn to_string(&self) -> String { + String::from(to_string_expr_wrap_in_deref!(self ; $($x)*)) + } + } + )+ + }; +} + +#[cfg(not(no_global_oom_handling))] +to_string_str! { + x x x x x x x x x x x x, + x x x x x x x x x x x, + x x x x x x x x x x, + x x x x x x x x x, + x x x x x x x x, + x x x x x x x, + x x x x x x, + x x x x x, + x x x x, + x x x, + x x, + x, } #[doc(hidden)] diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index a905a1e6b7e62..43684f31cb723 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -9,26 +9,24 @@ //! `#[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}; -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; +use core::pin::{Pin, PinCoerceUnsized}; use core::ptr::{self, NonNull}; #[cfg(not(no_global_oom_handling))] 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; @@ -337,7 +335,7 @@ impl, U: ?Sized, A: Allocator> CoerceUnsized> f impl, U: ?Sized> DispatchFromDyn> for Weak {} #[stable(feature = "arc_weak", since = "1.4.0")] -impl fmt::Debug for Weak { +impl fmt::Debug for Weak { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "(Weak)") } @@ -428,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. @@ -438,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() /// } @@ -507,7 +505,6 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(get_mut_unchecked)] /// /// use std::sync::Arc; @@ -523,7 +520,7 @@ impl Arc { /// ``` #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_uninit", issue = "63291")] + #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use] pub fn new_uninit() -> Arc> { unsafe { @@ -544,7 +541,7 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(new_uninit)] + /// #![feature(new_zeroed_alloc)] /// /// use std::sync::Arc; /// @@ -557,7 +554,7 @@ impl Arc { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_uninit", issue = "63291")] + #[unstable(feature = "new_zeroed_alloc", issue = "129396")] #[must_use] pub fn new_zeroed() -> Arc> { unsafe { @@ -615,7 +612,7 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(new_uninit, allocator_api)] + /// #![feature(allocator_api)] /// #![feature(get_mut_unchecked)] /// /// use std::sync::Arc; @@ -651,7 +648,7 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(new_uninit, allocator_api)] + /// #![feature( allocator_api)] /// /// use std::sync::Arc; /// @@ -712,7 +709,6 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(get_mut_unchecked)] /// #![feature(allocator_api)] /// @@ -756,7 +752,6 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(allocator_api)] /// /// use std::sync::Arc; @@ -846,7 +841,7 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(new_uninit, allocator_api)] + /// #![feature(allocator_api)] /// #![feature(get_mut_unchecked)] /// /// use std::sync::Arc; @@ -890,7 +885,7 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(new_uninit, allocator_api)] + /// #![feature(allocator_api)] /// /// use std::sync::Arc; /// use std::alloc::System; @@ -960,16 +955,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. @@ -1104,7 +1097,6 @@ impl Arc<[T]> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(get_mut_unchecked)] /// /// use std::sync::Arc; @@ -1123,7 +1115,7 @@ impl Arc<[T]> { /// ``` #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_uninit", issue = "63291")] + #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use] pub fn new_uninit_slice(len: usize) -> Arc<[mem::MaybeUninit]> { unsafe { Arc::from_ptr(Arc::allocate_for_slice(len)) } @@ -1138,7 +1130,7 @@ impl Arc<[T]> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] + /// #![feature(new_zeroed_alloc)] /// /// use std::sync::Arc; /// @@ -1151,7 +1143,7 @@ impl Arc<[T]> { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_uninit", issue = "63291")] + #[unstable(feature = "new_zeroed_alloc", issue = "129396")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Arc<[mem::MaybeUninit]> { unsafe { @@ -1174,7 +1166,6 @@ impl Arc<[T], A> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(get_mut_unchecked)] /// #![feature(allocator_api)] /// @@ -1195,7 +1186,7 @@ impl Arc<[T], A> { /// assert_eq!(*values, [1, 2, 3]) /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_uninit", issue = "63291")] + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn new_uninit_slice_in(len: usize, alloc: A) -> Arc<[mem::MaybeUninit], A> { unsafe { Arc::from_ptr_in(Arc::allocate_for_slice_in(len, &alloc), alloc) } @@ -1210,7 +1201,6 @@ impl Arc<[T], A> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(allocator_api)] /// /// use std::sync::Arc; @@ -1224,7 +1214,7 @@ impl Arc<[T], A> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_uninit", issue = "63291")] + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Arc<[mem::MaybeUninit], A> { unsafe { @@ -1259,7 +1249,6 @@ impl Arc, A> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(get_mut_unchecked)] /// /// use std::sync::Arc; @@ -1273,7 +1262,7 @@ impl Arc, A> { /// /// assert_eq!(*five, 5) /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] + #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use = "`self` will be dropped if the result is not used"] #[inline] pub unsafe fn assume_init(self) -> Arc { @@ -1298,7 +1287,6 @@ impl Arc<[mem::MaybeUninit], A> { /// # Examples /// /// ``` - /// #![feature(new_uninit)] /// #![feature(get_mut_unchecked)] /// /// use std::sync::Arc; @@ -1315,7 +1303,7 @@ impl Arc<[mem::MaybeUninit], A> { /// /// assert_eq!(*values, [1, 2, 3]) /// ``` - #[unstable(feature = "new_uninit", issue = "63291")] + #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use = "`self` will be dropped if the result is not used"] #[inline] pub unsafe fn assume_init(self) -> Arc<[T], A> { @@ -1493,9 +1481,8 @@ impl Arc { #[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. @@ -2147,6 +2134,12 @@ impl Deref for Arc { } } +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Arc {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Weak {} + #[unstable(feature = "deref_pure_trait", issue = "87121")] unsafe impl DerefPure for Arc {} @@ -2534,7 +2527,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 /// @@ -2801,9 +2794,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. @@ -3550,7 +3541,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 /// @@ -3569,7 +3560,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 /// @@ -3588,7 +3579,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 /// @@ -3626,7 +3617,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 /// @@ -3659,8 +3650,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 /// @@ -3816,7 +3807,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 /// @@ -3838,7 +3829,7 @@ fn data_offset_align(align: usize) -> usize { layout.size() + layout.padding_needed_for(align) } -/// A unique owning pointer to a [`ArcInner`] **that does not imply the contents are initialized,** +/// 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. @@ -3851,7 +3842,7 @@ struct UniqueArcUninit { #[cfg(not(no_global_oom_handling))] impl UniqueArcUninit { - /// Allocate a ArcInner with layout suitable to contain `for_value` or a clone of it. + /// 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 { @@ -3875,13 +3866,14 @@ impl UniqueArcUninit { /// # Safety /// /// The data must have been initialized (by writing to [`Self::data_ptr()`]). - unsafe fn into_arc(mut self) -> Arc { - let ptr = self.ptr; - let alloc = self.alloc.take().unwrap(); - mem::forget(self); + 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.as_ptr(), alloc) } + unsafe { Arc::from_ptr_in(ptr, alloc) } } } diff --git a/library/alloc/src/sync/tests.rs b/library/alloc/src/sync/tests.rs index 1b123aa58f205..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 { 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 ff72f99b2cbed..684bac60d9a86 100644 --- a/library/alloc/src/testing/crash_test.rs +++ b/library/alloc/src/testing/crash_test.rs @@ -1,6 +1,8 @@ -use crate::fmt::Debug; // the `Debug` trait is the only thing we use from `crate::fmt` 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 0dc193d82c535..d119e6ca397c5 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -155,9 +155,7 @@ //! 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}; @@ -165,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 10f62e4bb62d8..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,8 +114,9 @@ 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 /// diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 729d5dd4fe4d2..162791ba59d03 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) { @@ -1277,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. /// @@ -1333,11 +1334,11 @@ impl Vec { self.buf.ptr() } - /// Returns an unsafe mutable pointer to the vector's buffer, or a dangling + /// Returns a raw mutable pointer to the vector's buffer, or a dangling /// 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. /// @@ -1349,7 +1350,6 @@ impl Vec { /// may still invalidate this pointer. /// See the second example below for how this guarantee can be used. /// - /// /// # Examples /// /// ``` @@ -1519,6 +1519,7 @@ impl Vec { #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[track_caller] + #[optimize(size)] fn assert_failed(index: usize, len: usize) -> ! { panic!("swap_remove index (is {index}) should be < len (is {len})"); } @@ -1567,6 +1568,7 @@ impl Vec { #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[track_caller] + #[optimize(size)] fn assert_failed(index: usize, len: usize) -> ! { panic!("insertion index (is {index}) should be <= len (is {len})"); } @@ -1629,6 +1631,7 @@ impl Vec { #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[track_caller] + #[optimize(size)] fn assert_failed(index: usize, len: usize) -> ! { panic!("removal index (is {index}) should be < len (is {len})"); } @@ -1711,6 +1714,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) }; @@ -2311,6 +2320,7 @@ impl Vec { #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[track_caller] + #[optimize(size)] fn assert_failed(at: usize, len: usize) -> ! { panic!("`at` split index (is {at}) should be <= len (is {len})"); } @@ -2373,9 +2383,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 @@ -3359,7 +3371,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 /// @@ -3379,7 +3391,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 /// @@ -3399,7 +3411,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 /// @@ -3414,7 +3426,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 /// @@ -3429,7 +3441,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 /// @@ -3452,7 +3464,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 @@ -3475,7 +3487,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 @@ -3494,7 +3506,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`]. /// @@ -3522,7 +3534,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 c37a80dca95c8..dc27c578b57ef 100644 --- a/library/alloc/tests/arc.rs +++ b/library/alloc/tests/arc.rs @@ -227,3 +227,17 @@ fn make_mut_unsized() { assert_eq!(*data, [11, 21, 31]); assert_eq!(*other_data, [110, 20, 30]); } + +#[allow(unused)] +mod pin_coerce_unsized { + use alloc::sync::Arc; + use core::pin::Pin; + + pub trait MyTrait {} + impl MyTrait for String {} + + // Pin coercion should work for Arc + pub fn pin_arc(arg: Pin>) -> Pin> { + arg + } +} diff --git a/library/alloc/tests/boxed.rs b/library/alloc/tests/boxed.rs index 4cacee0414d7d..bfc31a626fadd 100644 --- a/library/alloc/tests/boxed.rs +++ b/library/alloc/tests/boxed.rs @@ -59,6 +59,7 @@ fn box_deref_lval() { assert_eq!(x.get(), 1000); } +#[allow(unused)] pub struct ConstAllocator; unsafe impl Allocator for ConstAllocator { @@ -179,3 +180,40 @@ unsafe impl Allocator for ConstAllocator { self } } + +#[allow(unused)] +mod pin_coerce_unsized { + use alloc::boxed::Box; + use core::pin::Pin; + + trait MyTrait { + fn action(&self) -> &str; + } + impl MyTrait for String { + fn action(&self) -> &str { + &*self + } + } + struct MyStruct; + impl MyTrait for MyStruct { + fn action(&self) -> &str { + "MyStruct" + } + } + + // Pin coercion should work for Box + fn pin_box(arg: Pin>) -> Pin> { + arg + } + + #[test] + fn pin_coerce_unsized_box() { + let my_string = "my string"; + let a_string = Box::pin(String::from(my_string)); + let pin_box_str = pin_box(a_string); + assert_eq!(pin_box_str.as_ref().action(), my_string); + let a_struct = Box::pin(MyStruct); + let pin_box_struct = pin_box(a_struct); + assert_eq!(pin_box_struct.as_ref().action(), "MyStruct"); + } +} 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/lib.rs b/library/alloc/tests/lib.rs index 89538f272f069..c5c6a122cfec8 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -15,7 +15,6 @@ #![feature(exact_size_is_empty)] #![feature(linked_list_cursors)] #![feature(map_try_insert)] -#![feature(new_uninit)] #![feature(pattern)] #![feature(trusted_len)] #![feature(try_reserve_kind)] @@ -40,6 +39,7 @@ #![feature(drain_keep_rest)] #![feature(local_waker)] #![feature(vec_pop_if)] +#![feature(unique_rc_arc)] #![allow(internal_features)] #![deny(fuzzy_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/library/alloc/tests/rc.rs b/library/alloc/tests/rc.rs index 499740e738ab0..29dbdcf225eb5 100644 --- a/library/alloc/tests/rc.rs +++ b/library/alloc/tests/rc.rs @@ -205,3 +205,20 @@ 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::rc::Weak` } + +#[allow(unused)] +mod pin_coerce_unsized { + use alloc::rc::{Rc, UniqueRc}; + use core::pin::Pin; + + pub trait MyTrait {} + impl MyTrait for String {} + + // Pin coercion should work for Rc + pub fn pin_rc(arg: Pin>) -> Pin> { + arg + } + pub fn pin_unique_rc(arg: Pin>) -> Pin> { + arg + } +} 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

>::Searcher: ReverseSearcher<'a>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { suffix.strip_suffix_of(self) } @@ -2273,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); @@ -2317,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) } @@ -2360,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) } @@ -2597,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. @@ -2646,7 +2626,7 @@ impl str { } } - /// 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 /// @@ -2684,7 +2664,7 @@ impl str { 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 /// @@ -2721,6 +2701,39 @@ impl str { pub fn escape_unicode(&self) -> EscapeUnicode<'_> { 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()) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2805,5 +2818,5 @@ impl_fn_for_zst! { } // This is required to make `impl From<&str> for Box` and `impl From for Box` not overlap. -#[stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "error_in_core_neg_impl", since = "1.65.0")] impl !crate::error::Error for &str {} diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index 8988229be2e57..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!( @@ -349,7 +348,7 @@ 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 3de5546c4d4e3..b69c476ae5e53 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -1,14 +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::range; use crate::slice::SliceIndex; use crate::ub_checks::assert_unsafe_precondition; - -use super::ParseBoolError; +use crate::{ops, ptr, range}; /// Implements ordering of strings. /// 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 efc07f38f68e0..b06a3bd4487d3 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -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 @@ -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 /// @@ -1080,7 +1077,7 @@ impl AtomicBool { /// assert_eq!(foo.load(Ordering::SeqCst), true); /// ``` #[inline] - #[stable(feature = "atomic_bool_fetch_not", since = "CURRENT_RUSTC_VERSION")] + #[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 { @@ -1276,7 +1273,7 @@ impl AtomicPtr { self.p.get_mut() } - /// Get atomic access to a pointer. + /// Gets atomic access to a pointer. /// /// # Examples /// @@ -1303,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. @@ -1343,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 /// @@ -3573,10 +3570,9 @@ unsafe fn atomic_umin(dst: *mut T, val: T, order: Ordering) -> T { /// An atomic fence. /// -/// Depending on the specified order, a fence prevents the compiler and CPU from -/// reordering certain types of memory operations around it. -/// That creates synchronizes-with relationships between it and atomic operations -/// or fences in other threads. +/// Fences create synchronization between themselves and atomic operations or fences in other +/// threads. To achieve this, a fence prevents the compiler and CPU from reordering certain types of +/// memory operations around it. /// /// A fence 'A' which has (at least) [`Release`] ordering semantics, synchronizes /// with a fence 'B' with (at least) [`Acquire`] semantics, if and only if there @@ -3597,6 +3593,12 @@ unsafe fn atomic_umin(dst: *mut T, val: T, order: Ordering) -> T { /// } /// ``` /// +/// Note that in the example above, it is crucial that the accesses to `x` are atomic. Fences cannot +/// be used to establish synchronization among non-atomic accesses in different threads. However, +/// thanks to the happens-before relationship between A and B, any non-atomic accesses that +/// happen-before A are now also properly synchronized with any non-atomic accesses that +/// happen-after B. +/// /// Atomic operations with [`Release`] or [`Acquire`] semantics can also synchronize /// with a fence. /// @@ -3662,33 +3664,30 @@ pub fn fence(order: Ordering) { } } -/// A compiler memory fence. +/// A "compiler-only" atomic fence. /// -/// `compiler_fence` does not emit any machine code, but restricts the kinds -/// of memory re-ordering the compiler is allowed to do. Specifically, depending on -/// the given [`Ordering`] semantics, the compiler may be disallowed from moving reads -/// or writes from before or after the call to the other side of the call to -/// `compiler_fence`. Note that it does **not** prevent the *hardware* -/// from doing such re-ordering. This is not a problem in a single-threaded, -/// execution context, but when other threads may modify memory at the same -/// time, stronger synchronization primitives such as [`fence`] are required. +/// Like [`fence`], this function establishes synchronization with other atomic operations and +/// fences. However, unlike [`fence`], `compiler_fence` only establishes synchronization with +/// operations *in the same thread*. This may at first sound rather useless, since code within a +/// thread is typically already totally ordered and does not need any further synchronization. +/// However, there are cases where code can run on the same thread without being ordered: +/// - The most common case is that of a *signal handler*: a signal handler runs in the same thread +/// as the code it interrupted, but it is not ordered with respect to that code. `compiler_fence` +/// can be used to establish synchronization between a thread and its signal handler, the same way +/// that `fence` can be used to establish synchronization across threads. +/// - Similar situations can arise in embedded programming with interrupt handlers, or in custom +/// implementations of preemptive green threads. In general, `compiler_fence` can establish +/// synchronization with code that is guaranteed to run on the same hardware CPU. /// -/// The re-ordering prevented by the different ordering semantics are: +/// See [`fence`] for how a fence can be used to achieve synchronization. Note that just like +/// [`fence`], synchronization still requires atomic operations to be used in both threads -- it is +/// not possible to perform synchronization entirely with fences and non-atomic operations. /// -/// - with [`SeqCst`], no re-ordering of reads and writes across this point is allowed. -/// - with [`Release`], preceding reads and writes cannot be moved past subsequent writes. -/// - with [`Acquire`], subsequent reads and writes cannot be moved ahead of preceding reads. -/// - with [`AcqRel`], both of the above rules are enforced. +/// `compiler_fence` does not emit any machine code, but restricts the kinds of memory re-ordering +/// the compiler is allowed to do. `compiler_fence` corresponds to [`atomic_signal_fence`] in C and +/// C++. /// -/// `compiler_fence` is generally only useful for preventing a thread from -/// racing *with itself*. That is, if a given thread is executing one piece -/// of code, and is then interrupted, and starts executing code elsewhere -/// (while still in the same thread, and conceptually still on the same -/// core). In traditional programs, this can only occur when a signal -/// handler is registered. In more low-level code, such situations can also -/// arise when handling interrupts, when implementing green threads with -/// pre-emption, etc. Curious readers are encouraged to read the Linux kernel's -/// discussion of [memory barriers]. +/// [`atomic_signal_fence`]: https://en.cppreference.com/w/cpp/atomic/atomic_signal_fence /// /// # Panics /// @@ -3726,8 +3725,6 @@ pub fn fence(order: Ordering) { /// } /// } /// ``` -/// -/// [memory barriers]: https://www.kernel.org/doc/Documentation/memory-barriers.txt #[inline] #[stable(feature = "compiler_fences", since = "1.21.0")] #[rustc_diagnostic_item = "compiler_fence"] 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/poll.rs b/library/core/src/task/poll.rs index bfa1cf096e237..6aab22177ab9d 100644 --- a/library/core/src/task/poll.rs +++ b/library/core/src/task/poll.rs @@ -5,6 +5,8 @@ use crate::ops::{self, ControlFlow}; /// Indicates whether a value is available or if the current task has been /// scheduled to receive a wakeup instead. +/// +/// This is returned by [`Future::poll`](core::future::Future::poll). #[must_use = "this `Poll` may be a `Pending` variant, which should be handled"] #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] #[lang = "Poll"] diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 86a965f68e085..5e559ad8d2ca7 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,22 +60,6 @@ impl RawWaker { RawWaker { data, vtable } } - /// Get the `data` pointer used to create this `RawWaker`. - #[inline] - #[must_use] - #[unstable(feature = "waker_getters", issue = "96992")] - pub fn data(&self) -> *const () { - self.data - } - - /// Get the `vtable` pointer used to create this `RawWaker`. - #[inline] - #[must_use] - #[unstable(feature = "waker_getters", issue = "96992")] - pub fn vtable(&self) -> &'static RawWakerVTable { - self.vtable - } - #[unstable(feature = "noop_waker", issue = "98286")] const NOOP: RawWaker = { const VTABLE: RawWakerVTable = RawWakerVTable::new( @@ -151,7 +133,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 +186,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 +232,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 = "1.82.0")] #[must_use] #[inline] pub const fn from_waker(waker: &'a Waker) -> Self { @@ -262,7 +245,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 = "1.82.0")] pub const fn waker(&self) -> &'a Waker { &self.waker } @@ -270,7 +253,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,7 +261,7 @@ 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? @@ -335,10 +318,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 = "1.82.0")] pub const fn from_waker(waker: &'a Waker) -> Self { // SAFETY: LocalWaker is just Waker without thread safety let local_waker = unsafe { transmute(waker) }; @@ -351,10 +334,10 @@ 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.0 { ExtData::Some(ext) => ExtData::Some(*ext), @@ -369,26 +352,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 +379,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 = "1.82.0")] 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 +412,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 +426,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 +448,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 @@ -505,11 +486,44 @@ impl Waker { #[must_use] #[stable(feature = "futures_api", since = "1.36.0")] pub fn will_wake(&self, other: &Waker) -> bool { + // We optimize this by comparing vtable addresses instead of vtable contents. + // This is permitted since the function is documented as best-effort. let RawWaker { data: a_data, vtable: a_vtable } = self.waker; let RawWaker { data: b_data, vtable: b_vtable } = other.waker; a_data == b_data && ptr::eq(a_vtable, b_vtable) } + /// Creates a new `Waker` from the provided `data` pointer and `vtable`. + /// + /// The `data` pointer can be used to store arbitrary data as required + /// by the executor. This could be e.g. a type-erased pointer to an `Arc` + /// that is associated with the task. + /// The value of this pointer will get passed to all functions that are part + /// of the `vtable` as the first parameter. + /// + /// It is important to consider that the `data` pointer must point to a + /// thread safe type such as an `Arc`. + /// + /// The `vtable` customizes the behavior of a `Waker`. For each operation + /// on the `Waker`, the associated function in the `vtable` will be called. + /// + /// # Safety + /// + /// The behavior of the returned `Waker` is undefined if the contract defined + /// in [`RawWakerVTable`]'s documentation is not upheld. + /// + /// (Authors wishing to avoid unsafe code may implement the [`Wake`] trait instead, at the + /// cost of a required heap allocation.) + /// + /// [`Wake`]: ../../alloc/task/trait.Wake.html + #[inline] + #[must_use] + #[stable(feature = "waker_getters", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "waker_getters", since = "CURRENT_RUSTC_VERSION")] + pub const unsafe fn new(data: *const (), vtable: &'static RawWakerVTable) -> Self { + Waker { waker: RawWaker { data, vtable } } + } + /// Creates a new `Waker` from [`RawWaker`]. /// /// # Safety @@ -524,17 +538,25 @@ 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 = "1.82.0")] pub const unsafe fn from_raw(waker: RawWaker) -> Waker { Waker { waker } } /// Returns a reference to a `Waker` that does nothing when used. /// + // Note! Much of the documentation for this method is duplicated + // in the docs for `LocalWaker::noop`. + // If you edit it, consider editing the other copy too. + // /// This is mostly useful for writing tests that need a [`Context`] to poll /// some futures, but are not expecting those futures to wake the waker or /// do not need to do anything specific if it happens. /// + /// More generally, using `Waker::noop()` to poll a future + /// means discarding the notification of when the future should be polled again. + /// So it should only be used when such a notification will not be needed to make progress. + /// /// If an owned `Waker` is needed, `clone()` this one. /// /// # Examples @@ -558,12 +580,20 @@ impl Waker { WAKER } - /// Get a reference to the underlying [`RawWaker`]. + /// Gets the `data` pointer used to create this `Waker`. #[inline] #[must_use] - #[unstable(feature = "waker_getters", issue = "96992")] - pub fn as_raw(&self) -> &RawWaker { - &self.waker + #[stable(feature = "waker_getters", since = "CURRENT_RUSTC_VERSION")] + pub fn data(&self) -> *const () { + self.waker.data + } + + /// Gets the `vtable` pointer used to create this `Waker`. + #[inline] + #[must_use] + #[stable(feature = "waker_getters", since = "CURRENT_RUSTC_VERSION")] + pub fn vtable(&self) -> &'static RawWakerVTable { + self.waker.vtable } } @@ -695,7 +725,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 +734,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 +756,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 @@ -766,7 +794,35 @@ impl LocalWaker { #[must_use] #[unstable(feature = "local_waker", issue = "118959")] pub fn will_wake(&self, other: &LocalWaker) -> bool { - self.waker == other.waker + // We optimize this by comparing vtable addresses instead of vtable contents. + // This is permitted since the function is documented as best-effort. + let RawWaker { data: a_data, vtable: a_vtable } = self.waker; + let RawWaker { data: b_data, vtable: b_vtable } = other.waker; + a_data == b_data && ptr::eq(a_vtable, b_vtable) + } + + /// Creates a new `LocalWaker` from the provided `data` pointer and `vtable`. + /// + /// The `data` pointer can be used to store arbitrary data as required + /// by the executor. This could be e.g. a type-erased pointer to an `Arc` + /// that is associated with the task. + /// The value of this pointer will get passed to all functions that are part + /// of the `vtable` as the first parameter. + /// + /// The `vtable` customizes the behavior of a `LocalWaker`. For each + /// operation on the `LocalWaker`, the associated function in the `vtable` + /// will be called. + /// + /// # Safety + /// + /// The behavior of the returned `Waker` is undefined if the contract defined + /// in [`RawWakerVTable`]'s documentation is not upheld. + /// + #[inline] + #[must_use] + #[unstable(feature = "local_waker", issue = "118959")] + pub const unsafe fn new(data: *const (), vtable: &'static RawWakerVTable) -> Self { + LocalWaker { waker: RawWaker { data, vtable } } } /// Creates a new `LocalWaker` from [`RawWaker`]. @@ -777,17 +833,27 @@ 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 } } - /// Creates a new `LocalWaker` that does nothing when `wake` is called. + /// Returns a reference to a `LocalWaker` that does nothing when used. /// + // Note! Much of the documentation for this method is duplicated + // in the docs for `Waker::noop`. + // If you edit it, consider editing the other copy too. + // /// This is mostly useful for writing tests that need a [`Context`] to poll /// some futures, but are not expecting those futures to wake the waker or /// do not need to do anything specific if it happens. /// + /// More generally, using `LocalWaker::noop()` to poll a future + /// means discarding the notification of when the future should be polled again, + /// So it should only be used when such a notification will not be needed to make progress. + /// + /// If an owned `LocalWaker` is needed, `clone()` this one. + /// /// # Examples /// /// ``` @@ -812,12 +878,20 @@ impl LocalWaker { WAKER } - /// Get a reference to the underlying [`RawWaker`]. + /// Gets the `data` pointer used to create this `LocalWaker`. #[inline] #[must_use] - #[unstable(feature = "waker_getters", issue = "96992")] - pub fn as_raw(&self) -> &RawWaker { - &self.waker + #[unstable(feature = "local_waker", issue = "118959")] + pub fn data(&self) -> *const () { + self.waker.data + } + + /// Gets the `vtable` pointer used to create this `LocalWaker`. + #[inline] + #[must_use] + #[unstable(feature = "local_waker", issue = "118959")] + pub fn vtable(&self) -> &'static RawWakerVTable { + self.waker.vtable } } #[unstable(feature = "local_waker", issue = "118959")] diff --git a/library/core/src/time.rs b/library/core/src/time.rs index d66f558078ea8..c19eeedb35426 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -250,7 +250,7 @@ impl Duration { /// ``` /// use std::time::Duration; /// - /// let duration = Duration::from_millis(2569); + /// let duration = Duration::from_millis(2_569); /// /// assert_eq!(2, duration.as_secs()); /// assert_eq!(569_000_000, duration.subsec_nanos()); @@ -279,7 +279,7 @@ impl Duration { /// let duration = Duration::from_micros(1_000_002); /// /// assert_eq!(1, duration.as_secs()); - /// assert_eq!(2000, duration.subsec_nanos()); + /// assert_eq!(2_000, duration.subsec_nanos()); /// ``` #[stable(feature = "duration_from_micros", since = "1.27.0")] #[must_use] @@ -472,7 +472,7 @@ impl Duration { /// ``` /// use std::time::Duration; /// - /// let duration = Duration::new(5, 730023852); + /// let duration = Duration::new(5, 730_023_852); /// assert_eq!(duration.as_secs(), 5); /// ``` /// @@ -501,7 +501,7 @@ impl Duration { /// ``` /// use std::time::Duration; /// - /// let duration = Duration::from_millis(5432); + /// let duration = Duration::from_millis(5_432); /// assert_eq!(duration.as_secs(), 5); /// assert_eq!(duration.subsec_millis(), 432); /// ``` @@ -547,7 +547,7 @@ impl Duration { /// ``` /// use std::time::Duration; /// - /// let duration = Duration::from_millis(5010); + /// let duration = Duration::from_millis(5_010); /// assert_eq!(duration.as_secs(), 5); /// assert_eq!(duration.subsec_nanos(), 10_000_000); /// ``` @@ -566,8 +566,8 @@ impl Duration { /// ``` /// use std::time::Duration; /// - /// let duration = Duration::new(5, 730023852); - /// assert_eq!(duration.as_millis(), 5730); + /// let duration = Duration::new(5, 730_023_852); + /// assert_eq!(duration.as_millis(), 5_730); /// ``` #[stable(feature = "duration_as_u128", since = "1.33.0")] #[rustc_const_stable(feature = "duration_as_u128", since = "1.33.0")] @@ -584,8 +584,8 @@ impl Duration { /// ``` /// use std::time::Duration; /// - /// let duration = Duration::new(5, 730023852); - /// assert_eq!(duration.as_micros(), 5730023); + /// let duration = Duration::new(5, 730_023_852); + /// assert_eq!(duration.as_micros(), 5_730_023); /// ``` #[stable(feature = "duration_as_u128", since = "1.33.0")] #[rustc_const_stable(feature = "duration_as_u128", since = "1.33.0")] @@ -602,8 +602,8 @@ impl Duration { /// ``` /// use std::time::Duration; /// - /// let duration = Duration::new(5, 730023852); - /// assert_eq!(duration.as_nanos(), 5730023852); + /// let duration = Duration::new(5, 730_023_852); + /// assert_eq!(duration.as_nanos(), 5_730_023_852); /// ``` #[stable(feature = "duration_as_u128", since = "1.33.0")] #[rustc_const_stable(feature = "duration_as_u128", since = "1.33.0")] @@ -617,16 +617,14 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// 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)); /// ``` - #[stable(feature = "duration_abs_diff", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "duration_abs_diff", since = "CURRENT_RUSTC_VERSION")] + #[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"] @@ -640,8 +638,6 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::time::Duration; /// @@ -700,8 +696,6 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::time::Duration; /// @@ -758,8 +752,6 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::time::Duration; /// @@ -814,8 +806,6 @@ impl Duration { /// /// # Examples /// - /// Basic usage: - /// /// ``` /// use std::time::Duration; /// @@ -889,7 +879,7 @@ impl Duration { /// use std::time::Duration; /// /// let dur = Duration::new(2, 345_678_000); - /// assert_eq!(dur.as_millis_f64(), 2345.678); + /// assert_eq!(dur.as_millis_f64(), 2_345.678); /// ``` #[unstable(feature = "duration_millis_float", issue = "122451")] #[must_use] @@ -910,7 +900,7 @@ impl Duration { /// use std::time::Duration; /// /// let dur = Duration::new(2, 345_678_000); - /// assert_eq!(dur.as_millis_f32(), 2345.678); + /// assert_eq!(dur.as_millis_f32(), 2_345.678); /// ``` #[unstable(feature = "duration_millis_float", issue = "122451")] #[must_use] @@ -1027,7 +1017,7 @@ impl Duration { /// /// let dur = Duration::new(2, 700_000_000); /// assert_eq!(dur.mul_f32(3.14), Duration::new(8, 478_000_641)); - /// assert_eq!(dur.mul_f32(3.14e5), Duration::new(847800, 0)); + /// assert_eq!(dur.mul_f32(3.14e5), Duration::new(847_800, 0)); /// ``` #[stable(feature = "duration_float", since = "1.38.0")] #[must_use = "this returns the result of the operation, \ @@ -1037,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. @@ -1058,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. @@ -1081,7 +1071,7 @@ 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 /// ``` @@ -1102,7 +1092,7 @@ impl Duration { self_nanos / rhs_nanos } - /// Divide `Duration` by `Duration` and return `f32`. + /// Divides `Duration` by `Duration` and returns `f32`. /// /// # Examples /// ``` diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 8e961d8adc372..206b5b9e2c24f 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,)+) {} } @@ -116,23 +122,29 @@ macro_rules! tuple_impls { } } - #[stable(feature = "array_tuple_conv", since = "1.71.0")] - impl From<[T; ${count($T)}]> for ($(${ignore($T)} T,)+) { - #[inline] - #[allow(non_snake_case)] - fn from(array: [T; ${count($T)}]) -> Self { - let [$($T,)+] = array; - ($($T,)+) + maybe_tuple_doc! { + $($T)+ @ + #[stable(feature = "array_tuple_conv", since = "1.71.0")] + impl From<[T; ${count($T)}]> for ($(${ignore($T)} T,)+) { + #[inline] + #[allow(non_snake_case)] + fn from(array: [T; ${count($T)}]) -> Self { + let [$($T,)+] = array; + ($($T,)+) + } } } - #[stable(feature = "array_tuple_conv", since = "1.71.0")] - impl From<($(${ignore($T)} T,)+)> for [T; ${count($T)}] { - #[inline] - #[allow(non_snake_case)] - fn from(tuple: ($(${ignore($T)} T,)+)) -> Self { - let ($($T,)+) = tuple; - [$($T,)+] + maybe_tuple_doc! { + $($T)+ @ + #[stable(feature = "array_tuple_conv", since = "1.71.0")] + impl From<($(${ignore($T)} T,)+)> for [T; ${count($T)}] { + #[inline] + #[allow(non_snake_case)] + fn from(tuple: ($(${ignore($T)} T,)+)) -> Self { + let ($($T,)+) = tuple; + [$($T,)+] + } } } } diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs index ae24b32e28d21..bd44edac869f4 100644 --- a/library/core/src/ub_checks.rs +++ b/library/core/src/ub_checks.rs @@ -3,12 +3,14 @@ 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)` +/// `assert_unsafe_precondition!(check_{library,language}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)` /// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all /// those arguments are passed to a function with the body `check_expr`. /// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB @@ -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 5ddd9f7476dd8..6066aa9921607 100644 --- a/library/core/src/unicode/mod.rs +++ b/library/core/src/unicode/mod.rs @@ -1,13 +1,15 @@ #![unstable(feature = "unicode_internals", issue = "none")] #![allow(missing_docs)] -// The `pub use` ones are for use in alloc, and are not re-exported in std. - -pub(crate) use unicode_data::alphabetic::lookup as Alphabetic; +// 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(crate) use unicode_data::cc::lookup as Cc; 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; 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/ascii_char.rs b/library/core/tests/ascii_char.rs new file mode 100644 index 0000000000000..75b5fd4b9e61d --- /dev/null +++ b/library/core/tests/ascii_char.rs @@ -0,0 +1,28 @@ +use core::ascii::Char; +use core::fmt::Write; + +/// Tests Display implementation for ascii::Char. +#[test] +fn test_display() { + let want = (0..128u8).map(|b| b as char).collect::(); + let mut got = String::with_capacity(128); + for byte in 0..128 { + write!(&mut got, "{}", Char::from_u8(byte).unwrap()).unwrap(); + } + assert_eq!(want, got); +} + +/// Tests Debug implementation for ascii::Char. +#[test] +fn test_debug_control() { + for byte in 0..128u8 { + let mut want = format!("{:?}", byte as char); + // `char` uses `'\u{#}'` representation where ascii::char uses `'\x##'`. + // Transform former into the latter. + if let Some(rest) = want.strip_prefix("'\\u{") { + want = format!("'\\x{:0>2}'", rest.strip_suffix("}'").unwrap()); + } + let chr = core::ascii::Char::from_u8(byte).unwrap(); + assert_eq!(want, format!("{chr:?}"), "byte: {byte}"); + } +} diff --git a/library/core/tests/clone.rs b/library/core/tests/clone.rs index 23efab2f1b598..71a328733b7c4 100644 --- a/library/core/tests/clone.rs +++ b/library/core/tests/clone.rs @@ -1,5 +1,7 @@ use core::clone::CloneToUninit; +use core::ffi::CStr; use core::mem::MaybeUninit; +use core::ptr; #[test] #[allow(suspicious_double_ref_op)] @@ -36,7 +38,8 @@ fn test_clone_to_uninit_slice_success() { #[test] #[cfg(panic = "unwind")] fn test_clone_to_uninit_slice_drops_on_panic() { - use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + 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. @@ -80,3 +83,41 @@ fn test_clone_to_uninit_slice_drops_on_panic() { drop(a); assert_eq!(COUNTER.load(Relaxed), 0); } + +#[test] +fn test_clone_to_uninit_str() { + let a = "hello"; + + let mut storage: MaybeUninit<[u8; 5]> = MaybeUninit::uninit(); + unsafe { a.clone_to_uninit(storage.as_mut_ptr() as *mut [u8] as *mut str) }; + assert_eq!(a.as_bytes(), unsafe { storage.assume_init() }.as_slice()); + + let mut b: Box = "world".into(); + assert_eq!(a.len(), b.len()); + assert_ne!(a, &*b); + unsafe { a.clone_to_uninit(ptr::from_mut::(&mut b)) }; + assert_eq!(a, &*b); +} + +#[test] +fn test_clone_to_uninit_cstr() { + let a = c"hello"; + + let mut storage: MaybeUninit<[u8; 6]> = MaybeUninit::uninit(); + unsafe { a.clone_to_uninit(storage.as_mut_ptr() as *mut [u8] as *mut CStr) }; + assert_eq!(a.to_bytes_with_nul(), unsafe { storage.assume_init() }.as_slice()); + + let mut b: Box = c"world".into(); + assert_eq!(a.count_bytes(), b.count_bytes()); + assert_ne!(a, &*b); + unsafe { a.clone_to_uninit(ptr::from_mut::(&mut b)) }; + assert_eq!(a, &*b); +} + +#[test] +fn cstr_metadata_is_length_with_nul() { + let s: &CStr = c"abcdef"; + let p: *const CStr = ptr::from_ref(s); + let bytes: *const [u8] = p as *const [u8]; + assert_eq!(s.to_bytes_with_nul().len(), bytes.len()); +} 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 2bdc334b7c027..ba4801f5912b8 100644 --- a/library/core/tests/fmt/builders.rs +++ b/library/core/tests/fmt/builders.rs @@ -79,23 +79,23 @@ mod debug_struct { } assert_eq!( - "Bar { foo: Foo { bar: true, baz: 10/20 }, hello: \"world\" }", + r#"Bar { foo: Foo { bar: true, baz: 10/20 }, hello: "world" }"#, format!("{Bar:?}") ); assert_eq!( - "Bar { + r#"Bar { foo: Foo { bar: true, baz: 10/20, }, - hello: \"world\", -}", + hello: "world", +}"#, format!("{Bar:#?}") ); } #[test] - fn test_only_non_exhaustive() { + fn test_empty_non_exhaustive() { struct Foo; impl fmt::Debug for Foo { @@ -157,19 +157,19 @@ mod debug_struct { } assert_eq!( - "Bar { foo: Foo { bar: true, baz: 10/20, .. }, hello: \"world\", .. }", + r#"Bar { foo: Foo { bar: true, baz: 10/20, .. }, hello: "world", .. }"#, format!("{Bar:?}") ); assert_eq!( - "Bar { + r#"Bar { foo: Foo { bar: true, baz: 10/20, .. }, - hello: \"world\", + hello: "world", .. -}", +}"#, format!("{Bar:#?}") ); } @@ -249,15 +249,89 @@ mod debug_tuple { } } - assert_eq!("Bar(Foo(true, 10/20), \"world\")", format!("{Bar:?}")); + assert_eq!(r#"Bar(Foo(true, 10/20), "world")"#, format!("{Bar:?}")); assert_eq!( - "Bar( + r#"Bar( Foo( true, 10/20, ), - \"world\", + "world", +)"#, + format!("{Bar:#?}") + ); + } + + #[test] + fn test_empty_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_tuple("Foo").finish_non_exhaustive() + } + } + + assert_eq!("Foo(..)", format!("{Foo:?}")); + assert_eq!("Foo(..)", format!("{Foo:#?}")); + } + + #[test] + fn test_multiple_and_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_tuple("Foo") + .field(&true) + .field(&format_args!("{}/{}", 10, 20)) + .finish_non_exhaustive() + } + } + + assert_eq!("Foo(true, 10/20, ..)", format!("{Foo:?}")); + assert_eq!( + "Foo( + true, + 10/20, + .. )", + format!("{Foo:#?}") + ); + } + + #[test] + fn test_nested_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_tuple("Foo") + .field(&true) + .field(&format_args!("{}/{}", 10, 20)) + .finish_non_exhaustive() + } + } + + struct Bar; + + impl fmt::Debug for Bar { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_tuple("Bar").field(&Foo).field(&"world").finish_non_exhaustive() + } + } + + assert_eq!(r#"Bar(Foo(true, 10/20, ..), "world", ..)"#, format!("{Bar:?}")); + assert_eq!( + r#"Bar( + Foo( + true, + 10/20, + .. + ), + "world", + .. +)"#, format!("{Bar:#?}") ); } @@ -301,11 +375,11 @@ mod debug_map { assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}")); assert_eq!(format!("{Entry:#?}"), format!("{KeyValue:#?}")); - assert_eq!("{\"bar\": true}", format!("{Entry:?}")); + assert_eq!(r#"{"bar": true}"#, format!("{Entry:?}")); assert_eq!( - "{ - \"bar\": true, -}", + r#"{ + "bar": true, +}"#, format!("{Entry:#?}") ); } @@ -339,12 +413,12 @@ mod debug_map { assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}")); assert_eq!(format!("{Entry:#?}"), format!("{KeyValue:#?}")); - assert_eq!("{\"bar\": true, 10: 10/20}", format!("{Entry:?}")); + assert_eq!(r#"{"bar": true, 10: 10/20}"#, format!("{Entry:?}")); assert_eq!( - "{ - \"bar\": true, + r#"{ + "bar": true, 10: 10/20, -}", +}"#, format!("{Entry:#?}") ); } @@ -371,21 +445,20 @@ mod debug_map { } assert_eq!( - "{\"foo\": {\"bar\": true, 10: 10/20}, \ - {\"bar\": true, 10: 10/20}: \"world\"}", + r#"{"foo": {"bar": true, 10: 10/20}, {"bar": true, 10: 10/20}: "world"}"#, format!("{Bar:?}") ); assert_eq!( - "{ - \"foo\": { - \"bar\": true, + r#"{ + "foo": { + "bar": true, 10: 10/20, }, { - \"bar\": true, + "bar": true, 10: 10/20, - }: \"world\", -}", + }: "world", +}"#, format!("{Bar:#?}") ); } @@ -471,6 +544,103 @@ mod debug_map { let _ = format!("{Foo:?}"); } + + #[test] + fn test_empty_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_map().finish_non_exhaustive() + } + } + + assert_eq!("{..}", format!("{Foo:?}")); + assert_eq!("{..}", format!("{Foo:#?}")); + } + + #[test] + fn test_multiple_and_non_exhaustive() { + struct Entry; + + impl fmt::Debug for Entry { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_map() + .entry(&"bar", &true) + .entry(&10, &format_args!("{}/{}", 10, 20)) + .finish_non_exhaustive() + } + } + + struct KeyValue; + + impl fmt::Debug for KeyValue { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_map() + .key(&"bar") + .value(&true) + .key(&10) + .value(&format_args!("{}/{}", 10, 20)) + .finish_non_exhaustive() + } + } + + assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}")); + assert_eq!(format!("{Entry:#?}"), format!("{KeyValue:#?}")); + + assert_eq!(r#"{"bar": true, 10: 10/20, ..}"#, format!("{Entry:?}")); + assert_eq!( + r#"{ + "bar": true, + 10: 10/20, + .. +}"#, + format!("{Entry:#?}") + ); + } + + #[test] + fn test_nested_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_map() + .entry(&"bar", &true) + .entry(&10, &format_args!("{}/{}", 10, 20)) + .finish_non_exhaustive() + } + } + + struct Bar; + + impl fmt::Debug for Bar { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_map().entry(&"foo", &Foo).entry(&Foo, &"world").finish_non_exhaustive() + } + } + + assert_eq!( + r#"{"foo": {"bar": true, 10: 10/20, ..}, {"bar": true, 10: 10/20, ..}: "world", ..}"#, + format!("{Bar:?}") + ); + assert_eq!( + r#"{ + "foo": { + "bar": true, + 10: 10/20, + .. + }, + { + "bar": true, + 10: 10/20, + .. + }: "world", + .. +}"#, + format!("{Bar:#?}") + ); + } } mod debug_set { @@ -547,15 +717,89 @@ mod debug_set { } } - assert_eq!("{{true, 10/20}, \"world\"}", format!("{Bar:?}")); + assert_eq!(r#"{{true, 10/20}, "world"}"#, format!("{Bar:?}")); assert_eq!( - "{ + r#"{ { true, 10/20, }, - \"world\", + "world", +}"#, + format!("{Bar:#?}") + ); + } + + #[test] + fn test_empty_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_set().finish_non_exhaustive() + } + } + + assert_eq!("{..}", format!("{Foo:?}")); + assert_eq!("{..}", format!("{Foo:#?}")); + } + + #[test] + fn test_multiple_and_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_set() + .entry(&true) + .entry(&format_args!("{}/{}", 10, 20)) + .finish_non_exhaustive() + } + } + + assert_eq!("{true, 10/20, ..}", format!("{Foo:?}")); + assert_eq!( + "{ + true, + 10/20, + .. }", + format!("{Foo:#?}") + ); + } + + #[test] + fn test_nested_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_set() + .entry(&true) + .entry(&format_args!("{}/{}", 10, 20)) + .finish_non_exhaustive() + } + } + + struct Bar; + + impl fmt::Debug for Bar { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_set().entry(&Foo).entry(&"world").finish_non_exhaustive() + } + } + + assert_eq!(r#"{{true, 10/20, ..}, "world", ..}"#, format!("{Bar:?}")); + assert_eq!( + r#"{ + { + true, + 10/20, + .. + }, + "world", + .. +}"#, format!("{Bar:#?}") ); } @@ -635,15 +879,89 @@ mod debug_list { } } - assert_eq!("[[true, 10/20], \"world\"]", format!("{Bar:?}")); + assert_eq!(r#"[[true, 10/20], "world"]"#, format!("{Bar:?}")); assert_eq!( - "[ + r#"[ [ true, 10/20, ], - \"world\", + "world", +]"#, + format!("{Bar:#?}") + ); + } + + #[test] + fn test_empty_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_list().finish_non_exhaustive() + } + } + + assert_eq!("[..]", format!("{Foo:?}")); + assert_eq!("[..]", format!("{Foo:#?}")); + } + + #[test] + fn test_multiple_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_list() + .entry(&true) + .entry(&format_args!("{}/{}", 10, 20)) + .finish_non_exhaustive() + } + } + + assert_eq!("[true, 10/20, ..]", format!("{Foo:?}")); + assert_eq!( + "[ + true, + 10/20, + .. ]", + format!("{Foo:#?}") + ); + } + + #[test] + fn test_nested_non_exhaustive() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_list() + .entry(&true) + .entry(&format_args!("{}/{}", 10, 20)) + .finish_non_exhaustive() + } + } + + struct Bar; + + impl fmt::Debug for Bar { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_list().entry(&Foo).entry(&"world").finish_non_exhaustive() + } + } + + assert_eq!(r#"[[true, 10/20, ..], "world", ..]"#, format!("{Bar:?}")); + assert_eq!( + r#"[ + [ + true, + 10/20, + .. + ], + "world", + .. +]"#, format!("{Bar:#?}") ); } 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 c93510df524cf..1b2c026ee1eb4 100644 --- a/library/core/tests/iter/adapters/chain.rs +++ b/library/core/tests/iter/adapters/chain.rs @@ -1,7 +1,8 @@ -use super::*; use core::iter::*; use core::num::NonZero; +use super::*; + #[test] fn test_chain() { let xs = [0, 1, 2, 3, 4, 5]; 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 6744eff3fa26f..01cebc9b27fd8 100644 --- a/library/core/tests/iter/adapters/map_windows.rs +++ b/library/core/tests/iter/adapters/map_windows.rs @@ -1,10 +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/take.rs b/library/core/tests/iter/adapters/take.rs index 39afa2cbfcaf2..65a8a93b4a916 100644 --- a/library/core/tests/iter/adapters/take.rs +++ b/library/core/tests/iter/adapters/take.rs @@ -170,3 +170,93 @@ fn test_byref_take_consumed_items() { assert_eq!(count, 70); assert_eq!(inner, 90..90); } + +#[test] +fn test_exact_size_take_repeat() { + let mut iter = core::iter::repeat(42).take(40); + assert_eq!((40, Some(40)), iter.size_hint()); + assert_eq!(40, iter.len()); + + assert_eq!(Some(42), iter.next()); + assert_eq!((39, Some(39)), iter.size_hint()); + assert_eq!(39, iter.len()); + + assert_eq!(Some(42), iter.next_back()); + assert_eq!((38, Some(38)), iter.size_hint()); + assert_eq!(38, iter.len()); + + assert_eq!(Some(42), iter.nth(3)); + assert_eq!((34, Some(34)), iter.size_hint()); + assert_eq!(34, iter.len()); + + assert_eq!(Some(42), iter.nth_back(3)); + assert_eq!((30, Some(30)), iter.size_hint()); + assert_eq!(30, iter.len()); + + assert_eq!(Ok(()), iter.advance_by(10)); + assert_eq!((20, Some(20)), iter.size_hint()); + assert_eq!(20, iter.len()); + + assert_eq!(Ok(()), iter.advance_back_by(10)); + assert_eq!((10, Some(10)), iter.size_hint()); + assert_eq!(10, iter.len()); +} + +#[test] +fn test_exact_size_take_repeat_with() { + let mut counter = 0; + let mut iter = core::iter::repeat_with(move || { + counter += 1; + counter + }) + .take(40); + assert_eq!((40, Some(40)), iter.size_hint()); + assert_eq!(40, iter.len()); + + assert_eq!(Some(1), iter.next()); + assert_eq!((39, Some(39)), iter.size_hint()); + assert_eq!(39, iter.len()); + + assert_eq!(Some(5), iter.nth(3)); + assert_eq!((35, Some(35)), iter.size_hint()); + assert_eq!(35, iter.len()); + + assert_eq!(Ok(()), iter.advance_by(10)); + assert_eq!((25, Some(25)), iter.size_hint()); + assert_eq!(25, iter.len()); + + assert_eq!(Some(16), iter.next()); + assert_eq!((24, Some(24)), iter.size_hint()); + assert_eq!(24, iter.len()); +} + +// This is https://github.com/rust-lang/rust/issues/104729 with all uses of +// repeat(0) were replaced by repeat(0).take(20). +#[test] +fn test_reverse_on_zip() { + let vec_1 = [1; 10]; + + let zipped_iter = vec_1.iter().copied().zip(core::iter::repeat(0).take(20)); + + // Forward + for (one, zero) in zipped_iter { + assert_eq!((1, 0), (one, zero)); + } + + let rev_vec_iter = vec_1.iter().rev(); + let rev_repeat_iter = std::iter::repeat(0).take(20).rev(); + + // Manual reversed zip + let rev_zipped_iter = rev_vec_iter.zip(rev_repeat_iter); + + for (&one, zero) in rev_zipped_iter { + assert_eq!((1, 0), (one, zero)); + } + + let zipped_iter = vec_1.iter().zip(core::iter::repeat(0).take(20)); + + // Cannot call rev here for automatic reversed zip constuction + for (&one, zero) in zipped_iter.rev() { + assert_eq!((1, 0), (one, zero)); + } +} 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 83a615fcd8be3..dbceb8abafc84 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -1,6 +1,10 @@ +// tidy-alphabetical-start +#![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)] @@ -9,119 +13,114 @@ #![feature(bigint_helper_methods)] #![feature(cell_update)] #![feature(clone_to_uninit)] -#![feature(const_align_offset)] #![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(debug_more_non_exhaustive)] #![feature(dec2flt)] -#![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(slice_take)] -#![feature(slice_from_ptr_range)] -#![feature(slice_split_once)] -#![feature(split_as_slice)] -#![feature(maybe_uninit_fill)] -#![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_repeat_n)] +#![feature(iter_partition_in_place)] #![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(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(waker_getters)] -#![feature(error_generic_member_access)] -#![feature(trait_upcasting)] -#![feature(is_ascii_octdigit)] -#![feature(get_many_mut)] -#![feature(iter_map_windows)] +#![feature(unsized_tuple_coercion)] +#![feature(unwrap_infallible)] +// 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; mod array; mod ascii; +mod ascii_char; mod asserting; mod async_iter; mod atomic; @@ -132,6 +131,7 @@ mod clone; mod cmp; mod const_ptr; mod convert; +mod ffi; mod fmt; mod future; mod hash; @@ -139,7 +139,6 @@ mod intrinsics; mod io; mod iter; mod lazy; -#[cfg(test)] mod macros; mod manually_drop; mod mem; diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs index cc73391630760..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; 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/num/float_iter_sum_identity.rs b/library/core/tests/num/float_iter_sum_identity.rs new file mode 100644 index 0000000000000..6d3224522a830 --- /dev/null +++ b/library/core/tests/num/float_iter_sum_identity.rs @@ -0,0 +1,27 @@ +#[test] +fn f32_ref() { + let x: f32 = -0.0; + let still_x: f32 = [x].iter().sum(); + assert_eq!(1. / x, 1. / still_x) +} + +#[test] +fn f32_own() { + let x: f32 = -0.0; + let still_x: f32 = [x].into_iter().sum(); + assert_eq!(1. / x, 1. / still_x) +} + +#[test] +fn f64_ref() { + let x: f64 = -0.0; + let still_x: f64 = [x].iter().sum(); + assert_eq!(1. / x, 1. / still_x) +} + +#[test] +fn f64_own() { + let x: f64 = -0.0; + let still_x: f64 = [x].into_iter().sum(); + assert_eq!(1. / x, 1. / still_x) +} 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/int_log.rs b/library/core/tests/num/int_log.rs index 2320a7acc35ac..60902752dab64 100644 --- a/library/core/tests/num/int_log.rs +++ b/library/core/tests/num/int_log.rs @@ -1,7 +1,4 @@ -//! This tests the `Integer::{ilog,log2,log10}` methods. These tests are in a -//! separate file because there's both a large number of them, and not all tests -//! can be run on Android. This is because in Android `ilog2` uses an imprecise -//! approximation:https://github.com/rust-lang/rust/blob/4825e12fc9c79954aa0fe18f5521efa6c19c7539/src/libstd/sys/unix/android.rs#L27-L53 +//! Tests for the `Integer::{ilog,log2,log10}` methods. #[test] fn checked_ilog() { @@ -48,6 +45,10 @@ fn checked_ilog2() { assert_eq!(0i8.checked_ilog2(), None); assert_eq!(0i16.checked_ilog2(), None); + assert_eq!(8192u16.checked_ilog2(), Some((8192f32).log2() as u32)); + assert_eq!(32768u16.checked_ilog2(), Some((32768f32).log2() as u32)); + assert_eq!(8192i16.checked_ilog2(), Some((8192f32).log2() as u32)); + for i in 1..=u8::MAX { assert_eq!(i.checked_ilog2(), Some((i as f32).log2() as u32), "checking {i}"); } @@ -77,15 +78,6 @@ fn checked_ilog2() { } } -// Validate cases that fail on Android's imprecise float ilog2 implementation. -#[test] -#[cfg(not(target_os = "android"))] -fn checked_ilog2_not_android() { - assert_eq!(8192u16.checked_ilog2(), Some((8192f32).log2() as u32)); - assert_eq!(32768u16.checked_ilog2(), Some((32768f32).log2() as u32)); - assert_eq!(8192i16.checked_ilog2(), Some((8192f32).log2() as u32)); -} - #[test] fn checked_ilog10() { assert_eq!(0u8.checked_ilog10(), None); diff --git a/library/core/tests/num/int_macros.rs b/library/core/tests/num/int_macros.rs index 165d9a296176e..830a96204ca03 100644 --- a/library/core/tests/num/int_macros.rs +++ b/library/core/tests/num/int_macros.rs @@ -1,427 +1,392 @@ macro_rules! int_module { ($T:ident) => { - #[cfg(test)] - mod tests { - use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; - use core::$T::*; - - use crate::num; - - #[test] - fn test_overflows() { - assert!(MAX > 0); - assert!(MIN <= 0); - assert_eq!(MIN + MAX + 1, 0); - } + use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; + use core::$T::*; - #[test] - fn test_num() { - num::test_num(10 as $T, 2 as $T); - } + use crate::num; - #[test] - fn test_rem_euclid() { - assert_eq!((-1 as $T).rem_euclid(MIN), MAX); - } + #[test] + fn test_overflows() { + assert!(MAX > 0); + assert!(MIN <= 0); + assert_eq!(MIN + MAX + 1, 0); + } - #[test] - pub fn test_abs() { - assert_eq!((1 as $T).abs(), 1 as $T); - assert_eq!((0 as $T).abs(), 0 as $T); - assert_eq!((-1 as $T).abs(), 1 as $T); - } + #[test] + fn test_num() { + num::test_num(10 as $T, 2 as $T); + } - #[test] - fn test_signum() { - assert_eq!((1 as $T).signum(), 1 as $T); - assert_eq!((0 as $T).signum(), 0 as $T); - assert_eq!((-0 as $T).signum(), 0 as $T); - assert_eq!((-1 as $T).signum(), -1 as $T); - } + #[test] + fn test_rem_euclid() { + assert_eq!((-1 as $T).rem_euclid(MIN), MAX); + } - #[test] - fn test_is_positive() { - assert!((1 as $T).is_positive()); - assert!(!(0 as $T).is_positive()); - assert!(!(-0 as $T).is_positive()); - assert!(!(-1 as $T).is_positive()); - } + #[test] + pub fn test_abs() { + assert_eq!((1 as $T).abs(), 1 as $T); + assert_eq!((0 as $T).abs(), 0 as $T); + assert_eq!((-1 as $T).abs(), 1 as $T); + } - #[test] - fn test_is_negative() { - assert!(!(1 as $T).is_negative()); - assert!(!(0 as $T).is_negative()); - assert!(!(-0 as $T).is_negative()); - assert!((-1 as $T).is_negative()); - } + #[test] + fn test_signum() { + assert_eq!((1 as $T).signum(), 1 as $T); + assert_eq!((0 as $T).signum(), 0 as $T); + assert_eq!((-0 as $T).signum(), 0 as $T); + assert_eq!((-1 as $T).signum(), -1 as $T); + } - #[test] - fn test_bitwise_operators() { - assert_eq!(0b1110 as $T, (0b1100 as $T).bitor(0b1010 as $T)); - assert_eq!(0b1000 as $T, (0b1100 as $T).bitand(0b1010 as $T)); - assert_eq!(0b0110 as $T, (0b1100 as $T).bitxor(0b1010 as $T)); - assert_eq!(0b1110 as $T, (0b0111 as $T).shl(1)); - assert_eq!(0b0111 as $T, (0b1110 as $T).shr(1)); - assert_eq!(-(0b11 as $T) - (1 as $T), (0b11 as $T).not()); - } + #[test] + fn test_is_positive() { + assert!((1 as $T).is_positive()); + assert!(!(0 as $T).is_positive()); + assert!(!(-0 as $T).is_positive()); + assert!(!(-1 as $T).is_positive()); + } - const A: $T = 0b0101100; - const B: $T = 0b0100001; - const C: $T = 0b1111001; + #[test] + fn test_is_negative() { + assert!(!(1 as $T).is_negative()); + assert!(!(0 as $T).is_negative()); + assert!(!(-0 as $T).is_negative()); + assert!((-1 as $T).is_negative()); + } - const _0: $T = 0; - const _1: $T = !0; + #[test] + fn test_bitwise_operators() { + assert_eq!(0b1110 as $T, (0b1100 as $T).bitor(0b1010 as $T)); + assert_eq!(0b1000 as $T, (0b1100 as $T).bitand(0b1010 as $T)); + assert_eq!(0b0110 as $T, (0b1100 as $T).bitxor(0b1010 as $T)); + assert_eq!(0b1110 as $T, (0b0111 as $T).shl(1)); + assert_eq!(0b0111 as $T, (0b1110 as $T).shr(1)); + assert_eq!(-(0b11 as $T) - (1 as $T), (0b11 as $T).not()); + } - #[test] - fn test_count_ones() { - assert_eq!(A.count_ones(), 3); - assert_eq!(B.count_ones(), 2); - assert_eq!(C.count_ones(), 5); - } + const A: $T = 0b0101100; + const B: $T = 0b0100001; + const C: $T = 0b1111001; - #[test] - fn test_count_zeros() { - assert_eq!(A.count_zeros(), $T::BITS - 3); - assert_eq!(B.count_zeros(), $T::BITS - 2); - assert_eq!(C.count_zeros(), $T::BITS - 5); - } + const _0: $T = 0; + const _1: $T = !0; - #[test] - fn test_leading_trailing_ones() { - let a: $T = 0b0101_1111; - assert_eq!(a.trailing_ones(), 5); - assert_eq!((!a).leading_ones(), $T::BITS - 7); + #[test] + fn test_count_ones() { + assert_eq!(A.count_ones(), 3); + assert_eq!(B.count_ones(), 2); + assert_eq!(C.count_ones(), 5); + } - assert_eq!(a.reverse_bits().leading_ones(), 5); + #[test] + fn test_count_zeros() { + assert_eq!(A.count_zeros(), $T::BITS - 3); + assert_eq!(B.count_zeros(), $T::BITS - 2); + assert_eq!(C.count_zeros(), $T::BITS - 5); + } - assert_eq!(_1.leading_ones(), $T::BITS); - assert_eq!(_1.trailing_ones(), $T::BITS); + #[test] + fn test_leading_trailing_ones() { + let a: $T = 0b0101_1111; + assert_eq!(a.trailing_ones(), 5); + assert_eq!((!a).leading_ones(), $T::BITS - 7); - assert_eq!((_1 << 1).trailing_ones(), 0); - assert_eq!(MAX.leading_ones(), 0); + assert_eq!(a.reverse_bits().leading_ones(), 5); - assert_eq!((_1 << 1).leading_ones(), $T::BITS - 1); - assert_eq!(MAX.trailing_ones(), $T::BITS - 1); + assert_eq!(_1.leading_ones(), $T::BITS); + assert_eq!(_1.trailing_ones(), $T::BITS); - assert_eq!(_0.leading_ones(), 0); - assert_eq!(_0.trailing_ones(), 0); + assert_eq!((_1 << 1).trailing_ones(), 0); + assert_eq!(MAX.leading_ones(), 0); - let x: $T = 0b0010_1100; - assert_eq!(x.leading_ones(), 0); - assert_eq!(x.trailing_ones(), 0); - } + assert_eq!((_1 << 1).leading_ones(), $T::BITS - 1); + assert_eq!(MAX.trailing_ones(), $T::BITS - 1); - #[test] - fn test_rotate() { - assert_eq!(A.rotate_left(6).rotate_right(2).rotate_right(4), A); - assert_eq!(B.rotate_left(3).rotate_left(2).rotate_right(5), B); - assert_eq!(C.rotate_left(6).rotate_right(2).rotate_right(4), C); - - // Rotating these should make no difference - // - // We test using 124 bits because to ensure that overlong bit shifts do - // not cause undefined behaviour. See #10183. - assert_eq!(_0.rotate_left(124), _0); - assert_eq!(_1.rotate_left(124), _1); - assert_eq!(_0.rotate_right(124), _0); - assert_eq!(_1.rotate_right(124), _1); - - // Rotating by 0 should have no effect - assert_eq!(A.rotate_left(0), A); - assert_eq!(B.rotate_left(0), B); - assert_eq!(C.rotate_left(0), C); - // Rotating by a multiple of word size should also have no effect - assert_eq!(A.rotate_left(128), A); - assert_eq!(B.rotate_left(128), B); - assert_eq!(C.rotate_left(128), C); - } + assert_eq!(_0.leading_ones(), 0); + assert_eq!(_0.trailing_ones(), 0); - #[test] - fn test_swap_bytes() { - assert_eq!(A.swap_bytes().swap_bytes(), A); - assert_eq!(B.swap_bytes().swap_bytes(), B); - assert_eq!(C.swap_bytes().swap_bytes(), C); + let x: $T = 0b0010_1100; + assert_eq!(x.leading_ones(), 0); + assert_eq!(x.trailing_ones(), 0); + } - // Swapping these should make no difference - assert_eq!(_0.swap_bytes(), _0); - assert_eq!(_1.swap_bytes(), _1); - } + #[test] + fn test_rotate() { + assert_eq!(A.rotate_left(6).rotate_right(2).rotate_right(4), A); + assert_eq!(B.rotate_left(3).rotate_left(2).rotate_right(5), B); + assert_eq!(C.rotate_left(6).rotate_right(2).rotate_right(4), C); + + // Rotating these should make no difference + // + // We test using 124 bits because to ensure that overlong bit shifts do + // not cause undefined behaviour. See #10183. + assert_eq!(_0.rotate_left(124), _0); + assert_eq!(_1.rotate_left(124), _1); + assert_eq!(_0.rotate_right(124), _0); + assert_eq!(_1.rotate_right(124), _1); + + // Rotating by 0 should have no effect + assert_eq!(A.rotate_left(0), A); + assert_eq!(B.rotate_left(0), B); + assert_eq!(C.rotate_left(0), C); + // Rotating by a multiple of word size should also have no effect + assert_eq!(A.rotate_left(128), A); + assert_eq!(B.rotate_left(128), B); + assert_eq!(C.rotate_left(128), C); + } - #[test] - fn test_le() { - assert_eq!($T::from_le(A.to_le()), A); - assert_eq!($T::from_le(B.to_le()), B); - assert_eq!($T::from_le(C.to_le()), C); - assert_eq!($T::from_le(_0), _0); - assert_eq!($T::from_le(_1), _1); - assert_eq!(_0.to_le(), _0); - assert_eq!(_1.to_le(), _1); - } + #[test] + fn test_swap_bytes() { + assert_eq!(A.swap_bytes().swap_bytes(), A); + assert_eq!(B.swap_bytes().swap_bytes(), B); + assert_eq!(C.swap_bytes().swap_bytes(), C); - #[test] - fn test_be() { - assert_eq!($T::from_be(A.to_be()), A); - assert_eq!($T::from_be(B.to_be()), B); - assert_eq!($T::from_be(C.to_be()), C); - assert_eq!($T::from_be(_0), _0); - assert_eq!($T::from_be(_1), _1); - assert_eq!(_0.to_be(), _0); - assert_eq!(_1.to_be(), _1); - } + // Swapping these should make no difference + assert_eq!(_0.swap_bytes(), _0); + assert_eq!(_1.swap_bytes(), _1); + } - #[test] - fn test_signed_checked_div() { - assert_eq!((10 as $T).checked_div(2), Some(5)); - assert_eq!((5 as $T).checked_div(0), None); - assert_eq!(isize::MIN.checked_div(-1), None); - } + #[test] + fn test_le() { + assert_eq!($T::from_le(A.to_le()), A); + assert_eq!($T::from_le(B.to_le()), B); + assert_eq!($T::from_le(C.to_le()), C); + assert_eq!($T::from_le(_0), _0); + assert_eq!($T::from_le(_1), _1); + assert_eq!(_0.to_le(), _0); + assert_eq!(_1.to_le(), _1); + } - #[test] - fn test_saturating_abs() { - assert_eq!((0 as $T).saturating_abs(), 0); - assert_eq!((123 as $T).saturating_abs(), 123); - assert_eq!((-123 as $T).saturating_abs(), 123); - assert_eq!((MAX - 2).saturating_abs(), MAX - 2); - assert_eq!((MAX - 1).saturating_abs(), MAX - 1); - assert_eq!(MAX.saturating_abs(), MAX); - assert_eq!((MIN + 2).saturating_abs(), MAX - 1); - assert_eq!((MIN + 1).saturating_abs(), MAX); - assert_eq!(MIN.saturating_abs(), MAX); - } + #[test] + fn test_be() { + assert_eq!($T::from_be(A.to_be()), A); + assert_eq!($T::from_be(B.to_be()), B); + assert_eq!($T::from_be(C.to_be()), C); + assert_eq!($T::from_be(_0), _0); + assert_eq!($T::from_be(_1), _1); + assert_eq!(_0.to_be(), _0); + assert_eq!(_1.to_be(), _1); + } - #[test] - fn test_saturating_neg() { - assert_eq!((0 as $T).saturating_neg(), 0); - assert_eq!((123 as $T).saturating_neg(), -123); - assert_eq!((-123 as $T).saturating_neg(), 123); - assert_eq!((MAX - 2).saturating_neg(), MIN + 3); - assert_eq!((MAX - 1).saturating_neg(), MIN + 2); - assert_eq!(MAX.saturating_neg(), MIN + 1); - assert_eq!((MIN + 2).saturating_neg(), MAX - 1); - assert_eq!((MIN + 1).saturating_neg(), MAX); - assert_eq!(MIN.saturating_neg(), MAX); - } + #[test] + fn test_signed_checked_div() { + assert_eq!((10 as $T).checked_div(2), Some(5)); + assert_eq!((5 as $T).checked_div(0), None); + assert_eq!(isize::MIN.checked_div(-1), None); + } - #[test] - fn test_from_str() { - fn from_str(t: &str) -> Option { - std::str::FromStr::from_str(t).ok() - } - assert_eq!(from_str::<$T>("0"), Some(0 as $T)); - assert_eq!(from_str::<$T>("3"), Some(3 as $T)); - assert_eq!(from_str::<$T>("10"), Some(10 as $T)); - assert_eq!(from_str::("123456789"), Some(123456789 as i32)); - assert_eq!(from_str::<$T>("00100"), Some(100 as $T)); - - assert_eq!(from_str::<$T>("-1"), Some(-1 as $T)); - assert_eq!(from_str::<$T>("-3"), Some(-3 as $T)); - assert_eq!(from_str::<$T>("-10"), Some(-10 as $T)); - assert_eq!(from_str::("-123456789"), Some(-123456789 as i32)); - assert_eq!(from_str::<$T>("-00100"), Some(-100 as $T)); - - assert_eq!(from_str::<$T>(""), None); - assert_eq!(from_str::<$T>(" "), None); - assert_eq!(from_str::<$T>("x"), None); - } + #[test] + fn test_saturating_abs() { + assert_eq!((0 as $T).saturating_abs(), 0); + assert_eq!((123 as $T).saturating_abs(), 123); + assert_eq!((-123 as $T).saturating_abs(), 123); + assert_eq!((MAX - 2).saturating_abs(), MAX - 2); + assert_eq!((MAX - 1).saturating_abs(), MAX - 1); + assert_eq!(MAX.saturating_abs(), MAX); + assert_eq!((MIN + 2).saturating_abs(), MAX - 1); + assert_eq!((MIN + 1).saturating_abs(), MAX); + assert_eq!(MIN.saturating_abs(), MAX); + } - #[test] - fn test_from_str_radix() { - assert_eq!($T::from_str_radix("123", 10), Ok(123 as $T)); - assert_eq!($T::from_str_radix("1001", 2), Ok(9 as $T)); - assert_eq!($T::from_str_radix("123", 8), Ok(83 as $T)); - assert_eq!(i32::from_str_radix("123", 16), Ok(291 as i32)); - assert_eq!(i32::from_str_radix("ffff", 16), Ok(65535 as i32)); - assert_eq!(i32::from_str_radix("FFFF", 16), Ok(65535 as i32)); - assert_eq!($T::from_str_radix("z", 36), Ok(35 as $T)); - assert_eq!($T::from_str_radix("Z", 36), Ok(35 as $T)); - - assert_eq!($T::from_str_radix("-123", 10), Ok(-123 as $T)); - assert_eq!($T::from_str_radix("-1001", 2), Ok(-9 as $T)); - assert_eq!($T::from_str_radix("-123", 8), Ok(-83 as $T)); - assert_eq!(i32::from_str_radix("-123", 16), Ok(-291 as i32)); - assert_eq!(i32::from_str_radix("-ffff", 16), Ok(-65535 as i32)); - assert_eq!(i32::from_str_radix("-FFFF", 16), Ok(-65535 as i32)); - assert_eq!($T::from_str_radix("-z", 36), Ok(-35 as $T)); - assert_eq!($T::from_str_radix("-Z", 36), Ok(-35 as $T)); - - assert_eq!($T::from_str_radix("Z", 35).ok(), None::<$T>); - assert_eq!($T::from_str_radix("-9", 2).ok(), None::<$T>); - } + #[test] + fn test_saturating_neg() { + assert_eq!((0 as $T).saturating_neg(), 0); + assert_eq!((123 as $T).saturating_neg(), -123); + assert_eq!((-123 as $T).saturating_neg(), 123); + assert_eq!((MAX - 2).saturating_neg(), MIN + 3); + assert_eq!((MAX - 1).saturating_neg(), MIN + 2); + assert_eq!(MAX.saturating_neg(), MIN + 1); + assert_eq!((MIN + 2).saturating_neg(), MAX - 1); + assert_eq!((MIN + 1).saturating_neg(), MAX); + assert_eq!(MIN.saturating_neg(), MAX); + } - #[test] - fn test_pow() { - let mut r = 2 as $T; - assert_eq!(r.pow(2), 4 as $T); - assert_eq!(r.pow(0), 1 as $T); - assert_eq!(r.wrapping_pow(2), 4 as $T); - assert_eq!(r.wrapping_pow(0), 1 as $T); - assert_eq!(r.checked_pow(2), Some(4 as $T)); - assert_eq!(r.checked_pow(0), Some(1 as $T)); - assert_eq!(r.overflowing_pow(2), (4 as $T, false)); - assert_eq!(r.overflowing_pow(0), (1 as $T, false)); - assert_eq!(r.saturating_pow(2), 4 as $T); - assert_eq!(r.saturating_pow(0), 1 as $T); - - r = MAX; - // use `^` to represent .pow() with no overflow. - // if itest::MAX == 2^j-1, then itest is a `j` bit int, - // so that `itest::MAX*itest::MAX == 2^(2*j)-2^(j+1)+1`, - // thussaturating_pow the overflowing result is exactly 1. - assert_eq!(r.wrapping_pow(2), 1 as $T); - assert_eq!(r.checked_pow(2), None); - assert_eq!(r.overflowing_pow(2), (1 as $T, true)); - assert_eq!(r.saturating_pow(2), MAX); - //test for negative exponent. - r = -2 as $T; - assert_eq!(r.pow(2), 4 as $T); - assert_eq!(r.pow(3), -8 as $T); - assert_eq!(r.pow(0), 1 as $T); - assert_eq!(r.wrapping_pow(2), 4 as $T); - assert_eq!(r.wrapping_pow(3), -8 as $T); - assert_eq!(r.wrapping_pow(0), 1 as $T); - assert_eq!(r.checked_pow(2), Some(4 as $T)); - assert_eq!(r.checked_pow(3), Some(-8 as $T)); - assert_eq!(r.checked_pow(0), Some(1 as $T)); - assert_eq!(r.overflowing_pow(2), (4 as $T, false)); - assert_eq!(r.overflowing_pow(3), (-8 as $T, false)); - assert_eq!(r.overflowing_pow(0), (1 as $T, false)); - assert_eq!(r.saturating_pow(2), 4 as $T); - assert_eq!(r.saturating_pow(3), -8 as $T); - assert_eq!(r.saturating_pow(0), 1 as $T); + #[test] + fn test_from_str() { + fn from_str(t: &str) -> Option { + std::str::FromStr::from_str(t).ok() } + assert_eq!(from_str::<$T>("0"), Some(0 as $T)); + assert_eq!(from_str::<$T>("3"), Some(3 as $T)); + assert_eq!(from_str::<$T>("10"), Some(10 as $T)); + assert_eq!(from_str::("123456789"), Some(123456789 as i32)); + assert_eq!(from_str::<$T>("00100"), Some(100 as $T)); + + assert_eq!(from_str::<$T>("-1"), Some(-1 as $T)); + assert_eq!(from_str::<$T>("-3"), Some(-3 as $T)); + assert_eq!(from_str::<$T>("-10"), Some(-10 as $T)); + assert_eq!(from_str::("-123456789"), Some(-123456789 as i32)); + assert_eq!(from_str::<$T>("-00100"), Some(-100 as $T)); + + assert_eq!(from_str::<$T>(""), None); + assert_eq!(from_str::<$T>(" "), None); + assert_eq!(from_str::<$T>("x"), None); + } - #[test] - fn test_isqrt() { - assert_eq!($T::MIN.checked_isqrt(), None); - assert_eq!((-1 as $T).checked_isqrt(), None); - assert_eq!((0 as $T).isqrt(), 0 as $T); - assert_eq!((1 as $T).isqrt(), 1 as $T); - assert_eq!((2 as $T).isqrt(), 1 as $T); - assert_eq!((99 as $T).isqrt(), 9 as $T); - assert_eq!((100 as $T).isqrt(), 10 as $T); - } + #[test] + fn test_from_str_radix() { + assert_eq!($T::from_str_radix("123", 10), Ok(123 as $T)); + assert_eq!($T::from_str_radix("1001", 2), Ok(9 as $T)); + assert_eq!($T::from_str_radix("123", 8), Ok(83 as $T)); + assert_eq!(i32::from_str_radix("123", 16), Ok(291 as i32)); + assert_eq!(i32::from_str_radix("ffff", 16), Ok(65535 as i32)); + assert_eq!(i32::from_str_radix("FFFF", 16), Ok(65535 as i32)); + assert_eq!($T::from_str_radix("z", 36), Ok(35 as $T)); + assert_eq!($T::from_str_radix("Z", 36), Ok(35 as $T)); + + assert_eq!($T::from_str_radix("-123", 10), Ok(-123 as $T)); + assert_eq!($T::from_str_radix("-1001", 2), Ok(-9 as $T)); + assert_eq!($T::from_str_radix("-123", 8), Ok(-83 as $T)); + assert_eq!(i32::from_str_radix("-123", 16), Ok(-291 as i32)); + assert_eq!(i32::from_str_radix("-ffff", 16), Ok(-65535 as i32)); + assert_eq!(i32::from_str_radix("-FFFF", 16), Ok(-65535 as i32)); + assert_eq!($T::from_str_radix("-z", 36), Ok(-35 as $T)); + assert_eq!($T::from_str_radix("-Z", 36), Ok(-35 as $T)); + + assert_eq!($T::from_str_radix("Z", 35).ok(), None::<$T>); + assert_eq!($T::from_str_radix("-9", 2).ok(), None::<$T>); + } - #[cfg(not(miri))] // Miri is too slow - #[test] - fn test_lots_of_isqrt() { - let n_max: $T = (1024 * 1024).min($T::MAX as u128) as $T; - for n in 0..=n_max { - let isqrt: $T = n.isqrt(); - - assert!(isqrt.pow(2) <= n); - let (square, overflow) = (isqrt + 1).overflowing_pow(2); - assert!(overflow || square > n); - } - - for n in ($T::MAX - 127)..=$T::MAX { - let isqrt: $T = n.isqrt(); - - assert!(isqrt.pow(2) <= n); - let (square, overflow) = (isqrt + 1).overflowing_pow(2); - assert!(overflow || square > n); - } - } + #[test] + fn test_pow() { + let mut r = 2 as $T; + assert_eq!(r.pow(2), 4 as $T); + assert_eq!(r.pow(0), 1 as $T); + assert_eq!(r.wrapping_pow(2), 4 as $T); + assert_eq!(r.wrapping_pow(0), 1 as $T); + assert_eq!(r.checked_pow(2), Some(4 as $T)); + assert_eq!(r.checked_pow(0), Some(1 as $T)); + assert_eq!(r.overflowing_pow(2), (4 as $T, false)); + assert_eq!(r.overflowing_pow(0), (1 as $T, false)); + assert_eq!(r.saturating_pow(2), 4 as $T); + assert_eq!(r.saturating_pow(0), 1 as $T); + + r = MAX; + // use `^` to represent .pow() with no overflow. + // if itest::MAX == 2^j-1, then itest is a `j` bit int, + // so that `itest::MAX*itest::MAX == 2^(2*j)-2^(j+1)+1`, + // thussaturating_pow the overflowing result is exactly 1. + assert_eq!(r.wrapping_pow(2), 1 as $T); + assert_eq!(r.checked_pow(2), None); + assert_eq!(r.overflowing_pow(2), (1 as $T, true)); + assert_eq!(r.saturating_pow(2), MAX); + //test for negative exponent. + r = -2 as $T; + assert_eq!(r.pow(2), 4 as $T); + assert_eq!(r.pow(3), -8 as $T); + assert_eq!(r.pow(0), 1 as $T); + assert_eq!(r.wrapping_pow(2), 4 as $T); + assert_eq!(r.wrapping_pow(3), -8 as $T); + assert_eq!(r.wrapping_pow(0), 1 as $T); + assert_eq!(r.checked_pow(2), Some(4 as $T)); + assert_eq!(r.checked_pow(3), Some(-8 as $T)); + assert_eq!(r.checked_pow(0), Some(1 as $T)); + assert_eq!(r.overflowing_pow(2), (4 as $T, false)); + assert_eq!(r.overflowing_pow(3), (-8 as $T, false)); + assert_eq!(r.overflowing_pow(0), (1 as $T, false)); + assert_eq!(r.saturating_pow(2), 4 as $T); + assert_eq!(r.saturating_pow(3), -8 as $T); + assert_eq!(r.saturating_pow(0), 1 as $T); + } - #[test] - fn test_div_floor() { - let a: $T = 8; - let b = 3; - assert_eq!(a.div_floor(b), 2); - assert_eq!(a.div_floor(-b), -3); - assert_eq!((-a).div_floor(b), -3); - assert_eq!((-a).div_floor(-b), 2); - } + #[test] + fn test_div_floor() { + let a: $T = 8; + let b = 3; + assert_eq!(a.div_floor(b), 2); + assert_eq!(a.div_floor(-b), -3); + assert_eq!((-a).div_floor(b), -3); + assert_eq!((-a).div_floor(-b), 2); + } - #[test] - fn test_div_ceil() { - let a: $T = 8; - let b = 3; - assert_eq!(a.div_ceil(b), 3); - assert_eq!(a.div_ceil(-b), -2); - assert_eq!((-a).div_ceil(b), -2); - assert_eq!((-a).div_ceil(-b), 3); - } + #[test] + fn test_div_ceil() { + let a: $T = 8; + let b = 3; + assert_eq!(a.div_ceil(b), 3); + assert_eq!(a.div_ceil(-b), -2); + assert_eq!((-a).div_ceil(b), -2); + assert_eq!((-a).div_ceil(-b), 3); + } - #[test] - fn test_next_multiple_of() { - assert_eq!((16 as $T).next_multiple_of(8), 16); - assert_eq!((23 as $T).next_multiple_of(8), 24); - assert_eq!((16 as $T).next_multiple_of(-8), 16); - assert_eq!((23 as $T).next_multiple_of(-8), 16); - assert_eq!((-16 as $T).next_multiple_of(8), -16); - assert_eq!((-23 as $T).next_multiple_of(8), -16); - assert_eq!((-16 as $T).next_multiple_of(-8), -16); - assert_eq!((-23 as $T).next_multiple_of(-8), -24); - assert_eq!(MIN.next_multiple_of(-1), MIN); - } + #[test] + fn test_next_multiple_of() { + assert_eq!((16 as $T).next_multiple_of(8), 16); + assert_eq!((23 as $T).next_multiple_of(8), 24); + assert_eq!((16 as $T).next_multiple_of(-8), 16); + assert_eq!((23 as $T).next_multiple_of(-8), 16); + assert_eq!((-16 as $T).next_multiple_of(8), -16); + assert_eq!((-23 as $T).next_multiple_of(8), -16); + assert_eq!((-16 as $T).next_multiple_of(-8), -16); + assert_eq!((-23 as $T).next_multiple_of(-8), -24); + assert_eq!(MIN.next_multiple_of(-1), MIN); + } - #[test] - fn test_checked_next_multiple_of() { - assert_eq!((16 as $T).checked_next_multiple_of(8), Some(16)); - assert_eq!((23 as $T).checked_next_multiple_of(8), Some(24)); - assert_eq!((16 as $T).checked_next_multiple_of(-8), Some(16)); - assert_eq!((23 as $T).checked_next_multiple_of(-8), Some(16)); - assert_eq!((-16 as $T).checked_next_multiple_of(8), Some(-16)); - assert_eq!((-23 as $T).checked_next_multiple_of(8), Some(-16)); - assert_eq!((-16 as $T).checked_next_multiple_of(-8), Some(-16)); - assert_eq!((-23 as $T).checked_next_multiple_of(-8), Some(-24)); - assert_eq!((1 as $T).checked_next_multiple_of(0), None); - assert_eq!(MAX.checked_next_multiple_of(2), None); - assert_eq!(MIN.checked_next_multiple_of(-3), None); - assert_eq!(MIN.checked_next_multiple_of(-1), Some(MIN)); - } + #[test] + fn test_checked_next_multiple_of() { + assert_eq!((16 as $T).checked_next_multiple_of(8), Some(16)); + assert_eq!((23 as $T).checked_next_multiple_of(8), Some(24)); + assert_eq!((16 as $T).checked_next_multiple_of(-8), Some(16)); + assert_eq!((23 as $T).checked_next_multiple_of(-8), Some(16)); + assert_eq!((-16 as $T).checked_next_multiple_of(8), Some(-16)); + assert_eq!((-23 as $T).checked_next_multiple_of(8), Some(-16)); + assert_eq!((-16 as $T).checked_next_multiple_of(-8), Some(-16)); + assert_eq!((-23 as $T).checked_next_multiple_of(-8), Some(-24)); + assert_eq!((1 as $T).checked_next_multiple_of(0), None); + assert_eq!(MAX.checked_next_multiple_of(2), None); + assert_eq!(MIN.checked_next_multiple_of(-3), None); + assert_eq!(MIN.checked_next_multiple_of(-1), Some(MIN)); + } - #[test] - fn test_carrying_add() { - assert_eq!($T::MAX.carrying_add(1, false), ($T::MIN, true)); - assert_eq!($T::MAX.carrying_add(0, true), ($T::MIN, true)); - assert_eq!($T::MAX.carrying_add(1, true), ($T::MIN + 1, true)); - assert_eq!($T::MAX.carrying_add(-1, false), ($T::MAX - 1, false)); - assert_eq!($T::MAX.carrying_add(-1, true), ($T::MAX, false)); // no intermediate overflow - assert_eq!($T::MIN.carrying_add(-1, false), ($T::MAX, true)); - assert_eq!($T::MIN.carrying_add(-1, true), ($T::MIN, false)); // no intermediate overflow - assert_eq!((0 as $T).carrying_add($T::MAX, true), ($T::MIN, true)); - assert_eq!((0 as $T).carrying_add($T::MIN, true), ($T::MIN + 1, false)); - } + #[test] + fn test_carrying_add() { + assert_eq!($T::MAX.carrying_add(1, false), ($T::MIN, true)); + assert_eq!($T::MAX.carrying_add(0, true), ($T::MIN, true)); + assert_eq!($T::MAX.carrying_add(1, true), ($T::MIN + 1, true)); + assert_eq!($T::MAX.carrying_add(-1, false), ($T::MAX - 1, false)); + assert_eq!($T::MAX.carrying_add(-1, true), ($T::MAX, false)); // no intermediate overflow + assert_eq!($T::MIN.carrying_add(-1, false), ($T::MAX, true)); + assert_eq!($T::MIN.carrying_add(-1, true), ($T::MIN, false)); // no intermediate overflow + assert_eq!((0 as $T).carrying_add($T::MAX, true), ($T::MIN, true)); + assert_eq!((0 as $T).carrying_add($T::MIN, true), ($T::MIN + 1, false)); + } - #[test] - fn test_borrowing_sub() { - assert_eq!($T::MIN.borrowing_sub(1, false), ($T::MAX, true)); - assert_eq!($T::MIN.borrowing_sub(0, true), ($T::MAX, true)); - assert_eq!($T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true)); - assert_eq!($T::MIN.borrowing_sub(-1, false), ($T::MIN + 1, false)); - assert_eq!($T::MIN.borrowing_sub(-1, true), ($T::MIN, false)); // no intermediate overflow - assert_eq!($T::MAX.borrowing_sub(-1, false), ($T::MIN, true)); - assert_eq!($T::MAX.borrowing_sub(-1, true), ($T::MAX, false)); // no intermediate overflow - assert_eq!((0 as $T).borrowing_sub($T::MIN, false), ($T::MIN, true)); - assert_eq!((0 as $T).borrowing_sub($T::MIN, true), ($T::MAX, false)); - } + #[test] + fn test_borrowing_sub() { + assert_eq!($T::MIN.borrowing_sub(1, false), ($T::MAX, true)); + assert_eq!($T::MIN.borrowing_sub(0, true), ($T::MAX, true)); + assert_eq!($T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true)); + assert_eq!($T::MIN.borrowing_sub(-1, false), ($T::MIN + 1, false)); + assert_eq!($T::MIN.borrowing_sub(-1, true), ($T::MIN, false)); // no intermediate overflow + assert_eq!($T::MAX.borrowing_sub(-1, false), ($T::MIN, true)); + assert_eq!($T::MAX.borrowing_sub(-1, true), ($T::MAX, false)); // no intermediate overflow + assert_eq!((0 as $T).borrowing_sub($T::MIN, false), ($T::MIN, true)); + assert_eq!((0 as $T).borrowing_sub($T::MIN, true), ($T::MAX, false)); + } - #[test] - fn test_midpoint() { - assert_eq!(<$T>::midpoint(1, 3), 2); - assert_eq!(<$T>::midpoint(3, 1), 2); - - assert_eq!(<$T>::midpoint(0, 0), 0); - assert_eq!(<$T>::midpoint(0, 2), 1); - assert_eq!(<$T>::midpoint(2, 0), 1); - assert_eq!(<$T>::midpoint(2, 2), 2); - - assert_eq!(<$T>::midpoint(1, 4), 2); - assert_eq!(<$T>::midpoint(4, 1), 2); - assert_eq!(<$T>::midpoint(3, 4), 3); - assert_eq!(<$T>::midpoint(4, 3), 3); - - assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), -1); - assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), -1); - assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN); - assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX); - - assert_eq!(<$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3); - assert_eq!(<$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3); - assert_eq!(<$T>::midpoint(<$T>::MAX, 6), <$T>::MAX / 2 + 3); - assert_eq!(<$T>::midpoint(6, <$T>::MAX), <$T>::MAX / 2 + 3); - } + #[test] + fn test_midpoint() { + assert_eq!(<$T>::midpoint(1, 3), 2); + assert_eq!(<$T>::midpoint(3, 1), 2); + + assert_eq!(<$T>::midpoint(0, 0), 0); + assert_eq!(<$T>::midpoint(0, 2), 1); + assert_eq!(<$T>::midpoint(2, 0), 1); + assert_eq!(<$T>::midpoint(2, 2), 2); + + assert_eq!(<$T>::midpoint(1, 4), 2); + assert_eq!(<$T>::midpoint(4, 1), 2); + assert_eq!(<$T>::midpoint(3, 4), 3); + assert_eq!(<$T>::midpoint(4, 3), 3); + + assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), -1); + assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), -1); + assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN); + assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX); + + assert_eq!(<$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3); + assert_eq!(<$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3); + assert_eq!(<$T>::midpoint(<$T>::MAX, 6), <$T>::MAX / 2 + 3); + assert_eq!(<$T>::midpoint(6, <$T>::MAX), <$T>::MAX / 2 + 3); } }; } diff --git a/library/core/tests/num/int_sqrt.rs b/library/core/tests/num/int_sqrt.rs new file mode 100644 index 0000000000000..d68db0787d22c --- /dev/null +++ b/library/core/tests/num/int_sqrt.rs @@ -0,0 +1,248 @@ +macro_rules! tests { + ($isqrt_consistency_check_fn_macro:ident : $($T:ident)+) => { + $( + mod $T { + $isqrt_consistency_check_fn_macro!($T); + + // Check that the following produce the correct values from + // `isqrt`: + // + // * the first and last 128 nonnegative values + // * powers of two, minus one + // * powers of two + // + // For signed types, check that `checked_isqrt` and `isqrt` + // either produce the same numeric value or respectively + // produce `None` and a panic. Make sure to do a consistency + // check for `<$T>::MIN` as well, as no nonnegative values + // negate to it. + // + // For unsigned types check that `isqrt` produces the same + // numeric value for `$T` and `NonZero<$T>`. + #[test] + fn isqrt() { + isqrt_consistency_check(<$T>::MIN); + + for n in (0..=127) + .chain(<$T>::MAX - 127..=<$T>::MAX) + .chain((0..<$T>::MAX.count_ones()).map(|exponent| (1 << exponent) - 1)) + .chain((0..<$T>::MAX.count_ones()).map(|exponent| 1 << exponent)) + { + isqrt_consistency_check(n); + + let isqrt_n = n.isqrt(); + assert!( + isqrt_n + .checked_mul(isqrt_n) + .map(|isqrt_n_squared| isqrt_n_squared <= n) + .unwrap_or(false), + "`{n}.isqrt()` should be lower than {isqrt_n}." + ); + assert!( + (isqrt_n + 1) + .checked_mul(isqrt_n + 1) + .map(|isqrt_n_plus_1_squared| n < isqrt_n_plus_1_squared) + .unwrap_or(true), + "`{n}.isqrt()` should be higher than {isqrt_n})." + ); + } + } + + // Check the square roots of: + // + // * the first 1,024 perfect squares + // * halfway between each of the first 1,024 perfect squares + // and the next perfect square + // * the next perfect square after the each of the first 1,024 + // perfect squares, minus one + // * the last 1,024 perfect squares + // * the last 1,024 perfect squares, minus one + // * halfway between each of the last 1,024 perfect squares + // and the previous perfect square + #[test] + // Skip this test on Miri, as it takes too long to run. + #[cfg(not(miri))] + fn isqrt_extended() { + // The correct value is worked out by using the fact that + // the nth nonzero perfect square is the sum of the first n + // odd numbers: + // + // 1 = 1 + // 4 = 1 + 3 + // 9 = 1 + 3 + 5 + // 16 = 1 + 3 + 5 + 7 + // + // Note also that the last odd number added in is two times + // the square root of the previous perfect square, plus + // one: + // + // 1 = 2*0 + 1 + // 3 = 2*1 + 1 + // 5 = 2*2 + 1 + // 7 = 2*3 + 1 + // + // That means we can add the square root of this perfect + // square once to get about halfway to the next perfect + // square, then we can add the square root of this perfect + // square again to get to the next perfect square, minus + // one, then we can add one to get to the next perfect + // square. + // + // This allows us to, for each of the first 1,024 perfect + // squares, test that the square roots of the following are + // all correct and equal to each other: + // + // * the current perfect square + // * about halfway to the next perfect square + // * the next perfect square, minus one + let mut n: $T = 0; + for sqrt_n in 0..1_024.min((1_u128 << (<$T>::MAX.count_ones()/2)) - 1) as $T { + isqrt_consistency_check(n); + assert_eq!( + n.isqrt(), + sqrt_n, + "`{sqrt_n}.pow(2).isqrt()` should be {sqrt_n}." + ); + + n += sqrt_n; + isqrt_consistency_check(n); + assert_eq!( + n.isqrt(), + sqrt_n, + "{n} is about halfway between `{sqrt_n}.pow(2)` and `{}.pow(2)`, so `{n}.isqrt()` should be {sqrt_n}.", + sqrt_n + 1 + ); + + n += sqrt_n; + isqrt_consistency_check(n); + assert_eq!( + n.isqrt(), + sqrt_n, + "`({}.pow(2) - 1).isqrt()` should be {sqrt_n}.", + sqrt_n + 1 + ); + + n += 1; + } + + // Similarly, for each of the last 1,024 perfect squares, + // check: + // + // * the current perfect square + // * the current perfect square, minus one + // * about halfway to the previous perfect square + // + // `MAX`'s `isqrt` return value is verified in the `isqrt` + // test function above. + let maximum_sqrt = <$T>::MAX.isqrt(); + let mut n = maximum_sqrt * maximum_sqrt; + + for sqrt_n in (maximum_sqrt - 1_024.min((1_u128 << (<$T>::MAX.count_ones()/2)) - 1) as $T..maximum_sqrt).rev() { + isqrt_consistency_check(n); + assert_eq!( + n.isqrt(), + sqrt_n + 1, + "`{0}.pow(2).isqrt()` should be {0}.", + sqrt_n + 1 + ); + + n -= 1; + isqrt_consistency_check(n); + assert_eq!( + n.isqrt(), + sqrt_n, + "`({}.pow(2) - 1).isqrt()` should be {sqrt_n}.", + sqrt_n + 1 + ); + + n -= sqrt_n; + isqrt_consistency_check(n); + assert_eq!( + n.isqrt(), + sqrt_n, + "{n} is about halfway between `{sqrt_n}.pow(2)` and `{}.pow(2)`, so `{n}.isqrt()` should be {sqrt_n}.", + sqrt_n + 1 + ); + + n -= sqrt_n; + } + } + } + )* + }; +} + +macro_rules! signed_check { + ($T:ident) => { + /// This takes an input and, if it's nonnegative or + #[doc = concat!("`", stringify!($T), "::MIN`,")] + /// checks that `isqrt` and `checked_isqrt` produce equivalent results + /// for that input and for the negative of that input. + /// + /// # Note + /// + /// This cannot check that negative inputs to `isqrt` cause panics if + /// panics abort instead of unwind. + fn isqrt_consistency_check(n: $T) { + // `<$T>::MIN` will be negative, so ignore it in this nonnegative + // section. + if n >= 0 { + assert_eq!( + Some(n.isqrt()), + n.checked_isqrt(), + "`{n}.checked_isqrt()` should match `Some({n}.isqrt())`.", + ); + } + + // `wrapping_neg` so that `<$T>::MIN` will negate to itself rather + // than panicking. + let negative_n = n.wrapping_neg(); + + // Zero negated will still be nonnegative, so ignore it in this + // negative section. + if negative_n < 0 { + assert_eq!( + negative_n.checked_isqrt(), + None, + "`({negative_n}).checked_isqrt()` should be `None`, as {negative_n} is negative.", + ); + + // `catch_unwind` only works when panics unwind rather than abort. + #[cfg(panic = "unwind")] + { + std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| (-n).isqrt())).expect_err( + &format!("`({negative_n}).isqrt()` should have panicked, as {negative_n} is negative.") + ); + } + } + } + }; +} + +macro_rules! unsigned_check { + ($T:ident) => { + /// This takes an input and, if it's nonzero, checks that `isqrt` + /// produces the same numeric value for both + #[doc = concat!("`", stringify!($T), "` and ")] + #[doc = concat!("`NonZero<", stringify!($T), ">`.")] + fn isqrt_consistency_check(n: $T) { + // Zero cannot be turned into a `NonZero` value, so ignore it in + // this nonzero section. + if n > 0 { + assert_eq!( + n.isqrt(), + core::num::NonZero::<$T>::new(n) + .expect( + "Was not able to create a new `NonZero` value from a nonzero number." + ) + .isqrt() + .get(), + "`{n}.isqrt` should match `NonZero`'s `{n}.isqrt().get()`.", + ); + } + } + }; +} + +tests!(signed_check: i8 i16 i32 i64 i128); +tests!(unsigned_check: u8 u16 u32 u64 u128); diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs index 9d2912c4b22dc..b14fe0b22c311 100644 --- a/library/core/tests/num/mod.rs +++ b/library/core/tests/num/mod.rs @@ -27,9 +27,11 @@ mod const_from; mod dec2flt; mod flt2dec; mod int_log; +mod int_sqrt; mod ops; mod wrapping; +mod float_iter_sum_identity; mod ieee754; mod nan; @@ -177,7 +179,7 @@ fn test_can_not_overflow() { // Check u128 separately: for base in 2..=36 { - let num = u128::MAX as u128; + let num = ::MAX; let max_len_string = format_radix(num, base as u128); // base 16 fits perfectly for u128 and won't overflow: assert_eq!(can_overflow::(base, &max_len_string), base != 16); diff --git a/library/core/tests/num/uint_macros.rs b/library/core/tests/num/uint_macros.rs index 955440647eb98..f4fa789461eb8 100644 --- a/library/core/tests/num/uint_macros.rs +++ b/library/core/tests/num/uint_macros.rs @@ -1,312 +1,317 @@ macro_rules! uint_module { ($T:ident) => { - #[cfg(test)] - mod tests { - use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; - use core::$T::*; - use std::str::FromStr; - - use crate::num; - - #[test] - fn test_overflows() { - assert!(MAX > 0); - assert!(MIN <= 0); - assert!((MIN + MAX).wrapping_add(1) == 0); - } + use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; + use core::$T::*; + use std::str::FromStr; - #[test] - fn test_num() { - num::test_num(10 as $T, 2 as $T); - } + use crate::num; - #[test] - fn test_bitwise_operators() { - assert!(0b1110 as $T == (0b1100 as $T).bitor(0b1010 as $T)); - assert!(0b1000 as $T == (0b1100 as $T).bitand(0b1010 as $T)); - assert!(0b0110 as $T == (0b1100 as $T).bitxor(0b1010 as $T)); - assert!(0b1110 as $T == (0b0111 as $T).shl(1)); - assert!(0b0111 as $T == (0b1110 as $T).shr(1)); - assert!(MAX - (0b1011 as $T) == (0b1011 as $T).not()); - } + #[test] + fn test_overflows() { + assert!(MAX > 0); + assert!(MIN <= 0); + assert!((MIN + MAX).wrapping_add(1) == 0); + } - const A: $T = 0b0101100; - const B: $T = 0b0100001; - const C: $T = 0b1111001; + #[test] + fn test_num() { + num::test_num(10 as $T, 2 as $T); + } - const _0: $T = 0; - const _1: $T = !0; + #[test] + fn test_bitwise_operators() { + assert!(0b1110 as $T == (0b1100 as $T).bitor(0b1010 as $T)); + assert!(0b1000 as $T == (0b1100 as $T).bitand(0b1010 as $T)); + assert!(0b0110 as $T == (0b1100 as $T).bitxor(0b1010 as $T)); + assert!(0b1110 as $T == (0b0111 as $T).shl(1)); + assert!(0b0111 as $T == (0b1110 as $T).shr(1)); + assert!(MAX - (0b1011 as $T) == (0b1011 as $T).not()); + } - #[test] - fn test_count_ones() { - assert!(A.count_ones() == 3); - assert!(B.count_ones() == 2); - assert!(C.count_ones() == 5); - } + const A: $T = 0b0101100; + const B: $T = 0b0100001; + const C: $T = 0b1111001; - #[test] - fn test_count_zeros() { - assert!(A.count_zeros() == $T::BITS - 3); - assert!(B.count_zeros() == $T::BITS - 2); - assert!(C.count_zeros() == $T::BITS - 5); - } + const _0: $T = 0; + const _1: $T = !0; - #[test] - fn test_leading_trailing_ones() { - let a: $T = 0b0101_1111; - assert_eq!(a.trailing_ones(), 5); - assert_eq!((!a).leading_ones(), $T::BITS - 7); + #[test] + fn test_count_ones() { + assert!(A.count_ones() == 3); + assert!(B.count_ones() == 2); + assert!(C.count_ones() == 5); + } - assert_eq!(a.reverse_bits().leading_ones(), 5); + #[test] + fn test_count_zeros() { + assert!(A.count_zeros() == $T::BITS - 3); + assert!(B.count_zeros() == $T::BITS - 2); + assert!(C.count_zeros() == $T::BITS - 5); + } - assert_eq!(_1.leading_ones(), $T::BITS); - assert_eq!(_1.trailing_ones(), $T::BITS); + #[test] + fn test_leading_trailing_ones() { + let a: $T = 0b0101_1111; + assert_eq!(a.trailing_ones(), 5); + assert_eq!((!a).leading_ones(), $T::BITS - 7); - assert_eq!((_1 << 1).trailing_ones(), 0); - assert_eq!((_1 >> 1).leading_ones(), 0); + assert_eq!(a.reverse_bits().leading_ones(), 5); - assert_eq!((_1 << 1).leading_ones(), $T::BITS - 1); - assert_eq!((_1 >> 1).trailing_ones(), $T::BITS - 1); + assert_eq!(_1.leading_ones(), $T::BITS); + assert_eq!(_1.trailing_ones(), $T::BITS); - assert_eq!(_0.leading_ones(), 0); - assert_eq!(_0.trailing_ones(), 0); + assert_eq!((_1 << 1).trailing_ones(), 0); + assert_eq!((_1 >> 1).leading_ones(), 0); - let x: $T = 0b0010_1100; - assert_eq!(x.leading_ones(), 0); - assert_eq!(x.trailing_ones(), 0); - } + assert_eq!((_1 << 1).leading_ones(), $T::BITS - 1); + assert_eq!((_1 >> 1).trailing_ones(), $T::BITS - 1); - #[test] - fn test_rotate() { - assert_eq!(A.rotate_left(6).rotate_right(2).rotate_right(4), A); - assert_eq!(B.rotate_left(3).rotate_left(2).rotate_right(5), B); - assert_eq!(C.rotate_left(6).rotate_right(2).rotate_right(4), C); - - // Rotating these should make no difference - // - // We test using 124 bits because to ensure that overlong bit shifts do - // not cause undefined behaviour. See #10183. - assert_eq!(_0.rotate_left(124), _0); - assert_eq!(_1.rotate_left(124), _1); - assert_eq!(_0.rotate_right(124), _0); - assert_eq!(_1.rotate_right(124), _1); - - // Rotating by 0 should have no effect - assert_eq!(A.rotate_left(0), A); - assert_eq!(B.rotate_left(0), B); - assert_eq!(C.rotate_left(0), C); - // Rotating by a multiple of word size should also have no effect - assert_eq!(A.rotate_left(128), A); - assert_eq!(B.rotate_left(128), B); - assert_eq!(C.rotate_left(128), C); - } + assert_eq!(_0.leading_ones(), 0); + assert_eq!(_0.trailing_ones(), 0); - #[test] - fn test_swap_bytes() { - assert_eq!(A.swap_bytes().swap_bytes(), A); - assert_eq!(B.swap_bytes().swap_bytes(), B); - assert_eq!(C.swap_bytes().swap_bytes(), C); + let x: $T = 0b0010_1100; + assert_eq!(x.leading_ones(), 0); + assert_eq!(x.trailing_ones(), 0); + } - // Swapping these should make no difference - assert_eq!(_0.swap_bytes(), _0); - assert_eq!(_1.swap_bytes(), _1); - } + #[test] + fn test_rotate() { + assert_eq!(A.rotate_left(6).rotate_right(2).rotate_right(4), A); + assert_eq!(B.rotate_left(3).rotate_left(2).rotate_right(5), B); + assert_eq!(C.rotate_left(6).rotate_right(2).rotate_right(4), C); + + // Rotating these should make no difference + // + // We test using 124 bits because to ensure that overlong bit shifts do + // not cause undefined behaviour. See #10183. + assert_eq!(_0.rotate_left(124), _0); + assert_eq!(_1.rotate_left(124), _1); + assert_eq!(_0.rotate_right(124), _0); + assert_eq!(_1.rotate_right(124), _1); + + // Rotating by 0 should have no effect + assert_eq!(A.rotate_left(0), A); + assert_eq!(B.rotate_left(0), B); + assert_eq!(C.rotate_left(0), C); + // Rotating by a multiple of word size should also have no effect + assert_eq!(A.rotate_left(128), A); + assert_eq!(B.rotate_left(128), B); + assert_eq!(C.rotate_left(128), C); + } - #[test] - fn test_reverse_bits() { - assert_eq!(A.reverse_bits().reverse_bits(), A); - assert_eq!(B.reverse_bits().reverse_bits(), B); - assert_eq!(C.reverse_bits().reverse_bits(), C); + #[test] + fn test_swap_bytes() { + assert_eq!(A.swap_bytes().swap_bytes(), A); + assert_eq!(B.swap_bytes().swap_bytes(), B); + assert_eq!(C.swap_bytes().swap_bytes(), C); - // Swapping these should make no difference - assert_eq!(_0.reverse_bits(), _0); - assert_eq!(_1.reverse_bits(), _1); - } + // Swapping these should make no difference + assert_eq!(_0.swap_bytes(), _0); + assert_eq!(_1.swap_bytes(), _1); + } - #[test] - fn test_le() { - assert_eq!($T::from_le(A.to_le()), A); - assert_eq!($T::from_le(B.to_le()), B); - assert_eq!($T::from_le(C.to_le()), C); - assert_eq!($T::from_le(_0), _0); - assert_eq!($T::from_le(_1), _1); - assert_eq!(_0.to_le(), _0); - assert_eq!(_1.to_le(), _1); - } + #[test] + fn test_reverse_bits() { + assert_eq!(A.reverse_bits().reverse_bits(), A); + assert_eq!(B.reverse_bits().reverse_bits(), B); + assert_eq!(C.reverse_bits().reverse_bits(), C); - #[test] - fn test_be() { - assert_eq!($T::from_be(A.to_be()), A); - assert_eq!($T::from_be(B.to_be()), B); - assert_eq!($T::from_be(C.to_be()), C); - assert_eq!($T::from_be(_0), _0); - assert_eq!($T::from_be(_1), _1); - assert_eq!(_0.to_be(), _0); - assert_eq!(_1.to_be(), _1); - } + // Swapping these should make no difference + assert_eq!(_0.reverse_bits(), _0); + assert_eq!(_1.reverse_bits(), _1); + } - #[test] - fn test_unsigned_checked_div() { - assert!((10 as $T).checked_div(2) == Some(5)); - assert!((5 as $T).checked_div(0) == None); - } + #[test] + fn test_le() { + assert_eq!($T::from_le(A.to_le()), A); + assert_eq!($T::from_le(B.to_le()), B); + assert_eq!($T::from_le(C.to_le()), C); + assert_eq!($T::from_le(_0), _0); + assert_eq!($T::from_le(_1), _1); + assert_eq!(_0.to_le(), _0); + assert_eq!(_1.to_le(), _1); + } - fn from_str(t: &str) -> Option { - FromStr::from_str(t).ok() - } + #[test] + fn test_be() { + assert_eq!($T::from_be(A.to_be()), A); + assert_eq!($T::from_be(B.to_be()), B); + assert_eq!($T::from_be(C.to_be()), C); + assert_eq!($T::from_be(_0), _0); + assert_eq!($T::from_be(_1), _1); + assert_eq!(_0.to_be(), _0); + assert_eq!(_1.to_be(), _1); + } - #[test] - pub fn test_from_str() { - assert_eq!(from_str::<$T>("0"), Some(0 as $T)); - assert_eq!(from_str::<$T>("3"), Some(3 as $T)); - assert_eq!(from_str::<$T>("10"), Some(10 as $T)); - assert_eq!(from_str::("123456789"), Some(123456789 as u32)); - assert_eq!(from_str::<$T>("00100"), Some(100 as $T)); - - assert_eq!(from_str::<$T>(""), None); - assert_eq!(from_str::<$T>(" "), None); - assert_eq!(from_str::<$T>("x"), None); - } + #[test] + fn test_unsigned_checked_div() { + assert!((10 as $T).checked_div(2) == Some(5)); + assert!((5 as $T).checked_div(0) == None); + } - #[test] - pub fn test_parse_bytes() { - assert_eq!($T::from_str_radix("123", 10), Ok(123 as $T)); - assert_eq!($T::from_str_radix("1001", 2), Ok(9 as $T)); - assert_eq!($T::from_str_radix("123", 8), Ok(83 as $T)); - assert_eq!(u16::from_str_radix("123", 16), Ok(291 as u16)); - assert_eq!(u16::from_str_radix("ffff", 16), Ok(65535 as u16)); - assert_eq!($T::from_str_radix("z", 36), Ok(35 as $T)); - - assert_eq!($T::from_str_radix("Z", 10).ok(), None::<$T>); - assert_eq!($T::from_str_radix("_", 2).ok(), None::<$T>); - } + fn from_str(t: &str) -> Option { + FromStr::from_str(t).ok() + } - #[test] - fn test_pow() { - let mut r = 2 as $T; - assert_eq!(r.pow(2), 4 as $T); - assert_eq!(r.pow(0), 1 as $T); - assert_eq!(r.wrapping_pow(2), 4 as $T); - assert_eq!(r.wrapping_pow(0), 1 as $T); - assert_eq!(r.checked_pow(2), Some(4 as $T)); - assert_eq!(r.checked_pow(0), Some(1 as $T)); - assert_eq!(r.overflowing_pow(2), (4 as $T, false)); - assert_eq!(r.overflowing_pow(0), (1 as $T, false)); - assert_eq!(r.saturating_pow(2), 4 as $T); - assert_eq!(r.saturating_pow(0), 1 as $T); - - r = MAX; - // use `^` to represent .pow() with no overflow. - // if itest::MAX == 2^j-1, then itest is a `j` bit int, - // so that `itest::MAX*itest::MAX == 2^(2*j)-2^(j+1)+1`, - // thussaturating_pow the overflowing result is exactly 1. - assert_eq!(r.wrapping_pow(2), 1 as $T); - assert_eq!(r.checked_pow(2), None); - assert_eq!(r.overflowing_pow(2), (1 as $T, true)); - assert_eq!(r.saturating_pow(2), MAX); - } + #[test] + pub fn test_from_str() { + assert_eq!(from_str::<$T>("0"), Some(0 as $T)); + assert_eq!(from_str::<$T>("3"), Some(3 as $T)); + assert_eq!(from_str::<$T>("10"), Some(10 as $T)); + assert_eq!(from_str::("123456789"), Some(123456789 as u32)); + assert_eq!(from_str::<$T>("00100"), Some(100 as $T)); + + assert_eq!(from_str::<$T>(""), None); + assert_eq!(from_str::<$T>(" "), None); + assert_eq!(from_str::<$T>("x"), None); + } - #[test] - fn test_isqrt() { - assert_eq!((0 as $T).isqrt(), 0 as $T); - assert_eq!((1 as $T).isqrt(), 1 as $T); - assert_eq!((2 as $T).isqrt(), 1 as $T); - assert_eq!((99 as $T).isqrt(), 9 as $T); - assert_eq!((100 as $T).isqrt(), 10 as $T); - assert_eq!($T::MAX.isqrt(), (1 << ($T::BITS / 2)) - 1); - } + #[test] + pub fn test_parse_bytes() { + assert_eq!($T::from_str_radix("123", 10), Ok(123 as $T)); + assert_eq!($T::from_str_radix("1001", 2), Ok(9 as $T)); + assert_eq!($T::from_str_radix("123", 8), Ok(83 as $T)); + assert_eq!(u16::from_str_radix("123", 16), Ok(291 as u16)); + assert_eq!(u16::from_str_radix("ffff", 16), Ok(65535 as u16)); + assert_eq!($T::from_str_radix("z", 36), Ok(35 as $T)); + + assert_eq!($T::from_str_radix("Z", 10).ok(), None::<$T>); + assert_eq!($T::from_str_radix("_", 2).ok(), None::<$T>); + } - #[cfg(not(miri))] // Miri is too slow - #[test] - fn test_lots_of_isqrt() { - let n_max: $T = (1024 * 1024).min($T::MAX as u128) as $T; - for n in 0..=n_max { - let isqrt: $T = n.isqrt(); + #[test] + fn test_pow() { + let mut r = 2 as $T; + assert_eq!(r.pow(2), 4 as $T); + assert_eq!(r.pow(0), 1 as $T); + assert_eq!(r.wrapping_pow(2), 4 as $T); + assert_eq!(r.wrapping_pow(0), 1 as $T); + assert_eq!(r.checked_pow(2), Some(4 as $T)); + assert_eq!(r.checked_pow(0), Some(1 as $T)); + assert_eq!(r.overflowing_pow(2), (4 as $T, false)); + assert_eq!(r.overflowing_pow(0), (1 as $T, false)); + assert_eq!(r.saturating_pow(2), 4 as $T); + assert_eq!(r.saturating_pow(0), 1 as $T); + + r = MAX; + // use `^` to represent .pow() with no overflow. + // if itest::MAX == 2^j-1, then itest is a `j` bit int, + // so that `itest::MAX*itest::MAX == 2^(2*j)-2^(j+1)+1`, + // thussaturating_pow the overflowing result is exactly 1. + assert_eq!(r.wrapping_pow(2), 1 as $T); + assert_eq!(r.checked_pow(2), None); + assert_eq!(r.overflowing_pow(2), (1 as $T, true)); + assert_eq!(r.saturating_pow(2), MAX); + } - assert!(isqrt.pow(2) <= n); - assert!(isqrt + 1 == (1 as $T) << ($T::BITS / 2) || (isqrt + 1).pow(2) > n); - } + #[test] + fn test_isqrt() { + assert_eq!((0 as $T).isqrt(), 0 as $T); + assert_eq!((1 as $T).isqrt(), 1 as $T); + assert_eq!((2 as $T).isqrt(), 1 as $T); + assert_eq!((99 as $T).isqrt(), 9 as $T); + assert_eq!((100 as $T).isqrt(), 10 as $T); + assert_eq!($T::MAX.isqrt(), (1 << ($T::BITS / 2)) - 1); + } - for n in ($T::MAX - 255)..=$T::MAX { - let isqrt: $T = n.isqrt(); + #[cfg(not(miri))] // Miri is too slow + #[test] + fn test_lots_of_isqrt() { + let n_max: $T = (1024 * 1024).min($T::MAX as u128) as $T; + for n in 0..=n_max { + let isqrt: $T = n.isqrt(); - assert!(isqrt.pow(2) <= n); - assert!(isqrt + 1 == (1 as $T) << ($T::BITS / 2) || (isqrt + 1).pow(2) > n); - } + assert!(isqrt.pow(2) <= n); + assert!(isqrt + 1 == (1 as $T) << ($T::BITS / 2) || (isqrt + 1).pow(2) > n); } - #[test] - fn test_div_floor() { - assert_eq!((8 as $T).div_floor(3), 2); - } + for n in ($T::MAX - 255)..=$T::MAX { + let isqrt: $T = n.isqrt(); - #[test] - fn test_div_ceil() { - assert_eq!((8 as $T).div_ceil(3), 3); + assert!(isqrt.pow(2) <= n); + assert!(isqrt + 1 == (1 as $T) << ($T::BITS / 2) || (isqrt + 1).pow(2) > n); } + } - #[test] - fn test_next_multiple_of() { - assert_eq!((16 as $T).next_multiple_of(8), 16); - assert_eq!((23 as $T).next_multiple_of(8), 24); - assert_eq!(MAX.next_multiple_of(1), MAX); - } + #[test] + fn test_div_floor() { + assert_eq!((8 as $T).div_floor(3), 2); + } - #[test] - fn test_checked_next_multiple_of() { - assert_eq!((16 as $T).checked_next_multiple_of(8), Some(16)); - assert_eq!((23 as $T).checked_next_multiple_of(8), Some(24)); - assert_eq!((1 as $T).checked_next_multiple_of(0), None); - assert_eq!(MAX.checked_next_multiple_of(2), None); - } + #[test] + fn test_div_ceil() { + assert_eq!((8 as $T).div_ceil(3), 3); + } - #[test] - fn test_carrying_add() { - assert_eq!($T::MAX.carrying_add(1, false), (0, true)); - assert_eq!($T::MAX.carrying_add(0, true), (0, true)); - assert_eq!($T::MAX.carrying_add(1, true), (1, true)); + #[test] + fn test_next_multiple_of() { + assert_eq!((16 as $T).next_multiple_of(8), 16); + assert_eq!((23 as $T).next_multiple_of(8), 24); + assert_eq!(MAX.next_multiple_of(1), MAX); + } - assert_eq!($T::MIN.carrying_add($T::MAX, false), ($T::MAX, false)); - assert_eq!($T::MIN.carrying_add(0, true), (1, false)); - assert_eq!($T::MIN.carrying_add($T::MAX, true), (0, true)); - } + #[test] + fn test_checked_next_multiple_of() { + assert_eq!((16 as $T).checked_next_multiple_of(8), Some(16)); + assert_eq!((23 as $T).checked_next_multiple_of(8), Some(24)); + assert_eq!((1 as $T).checked_next_multiple_of(0), None); + assert_eq!(MAX.checked_next_multiple_of(2), None); + } - #[test] - fn test_borrowing_sub() { - assert_eq!($T::MIN.borrowing_sub(1, false), ($T::MAX, true)); - assert_eq!($T::MIN.borrowing_sub(0, true), ($T::MAX, true)); - assert_eq!($T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true)); + #[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)); + } - assert_eq!($T::MAX.borrowing_sub($T::MAX, false), (0, false)); - assert_eq!($T::MAX.borrowing_sub(0, true), ($T::MAX - 1, false)); - assert_eq!($T::MAX.borrowing_sub($T::MAX, true), ($T::MAX, true)); - } + #[test] + fn test_carrying_add() { + assert_eq!($T::MAX.carrying_add(1, false), (0, true)); + assert_eq!($T::MAX.carrying_add(0, true), (0, true)); + assert_eq!($T::MAX.carrying_add(1, true), (1, true)); - #[test] - fn test_midpoint() { - assert_eq!(<$T>::midpoint(1, 3), 2); - assert_eq!(<$T>::midpoint(3, 1), 2); - - assert_eq!(<$T>::midpoint(0, 0), 0); - assert_eq!(<$T>::midpoint(0, 2), 1); - assert_eq!(<$T>::midpoint(2, 0), 1); - assert_eq!(<$T>::midpoint(2, 2), 2); - - assert_eq!(<$T>::midpoint(1, 4), 2); - assert_eq!(<$T>::midpoint(4, 1), 2); - assert_eq!(<$T>::midpoint(3, 4), 3); - assert_eq!(<$T>::midpoint(4, 3), 3); - - assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2); - assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), (<$T>::MAX - <$T>::MIN) / 2); - assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN); - assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX); - - assert_eq!(<$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3); - assert_eq!(<$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3); - assert_eq!(<$T>::midpoint(<$T>::MAX, 6), (<$T>::MAX - <$T>::MIN) / 2 + 3); - assert_eq!(<$T>::midpoint(6, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2 + 3); - } + assert_eq!($T::MIN.carrying_add($T::MAX, false), ($T::MAX, false)); + assert_eq!($T::MIN.carrying_add(0, true), (1, false)); + assert_eq!($T::MIN.carrying_add($T::MAX, true), (0, true)); + } + + #[test] + fn test_borrowing_sub() { + assert_eq!($T::MIN.borrowing_sub(1, false), ($T::MAX, true)); + assert_eq!($T::MIN.borrowing_sub(0, true), ($T::MAX, true)); + assert_eq!($T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true)); + + assert_eq!($T::MAX.borrowing_sub($T::MAX, false), (0, false)); + assert_eq!($T::MAX.borrowing_sub(0, true), ($T::MAX - 1, false)); + assert_eq!($T::MAX.borrowing_sub($T::MAX, true), ($T::MAX, true)); + } + + #[test] + fn test_midpoint() { + assert_eq!(<$T>::midpoint(1, 3), 2); + assert_eq!(<$T>::midpoint(3, 1), 2); + + assert_eq!(<$T>::midpoint(0, 0), 0); + assert_eq!(<$T>::midpoint(0, 2), 1); + assert_eq!(<$T>::midpoint(2, 0), 1); + assert_eq!(<$T>::midpoint(2, 2), 2); + + assert_eq!(<$T>::midpoint(1, 4), 2); + assert_eq!(<$T>::midpoint(4, 1), 2); + assert_eq!(<$T>::midpoint(3, 4), 3); + assert_eq!(<$T>::midpoint(4, 3), 3); + + assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2); + assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), (<$T>::MAX - <$T>::MIN) / 2); + assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN); + assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX); + + assert_eq!(<$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3); + assert_eq!(<$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3); + assert_eq!(<$T>::midpoint(<$T>::MAX, 6), (<$T>::MAX - <$T>::MIN) / 2 + 3); + assert_eq!(<$T>::midpoint(6, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2 + 3); } }; } diff --git a/library/core/tests/ops.rs b/library/core/tests/ops.rs index 0c81cba35b3df..501e0f33fe4cc 100644 --- a/library/core/tests/ops.rs +++ b/library/core/tests/ops.rs @@ -1,7 +1,9 @@ mod control_flow; +mod from_residual; -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/ops/from_residual.rs b/library/core/tests/ops/from_residual.rs new file mode 100644 index 0000000000000..d5c86ccbcd317 --- /dev/null +++ b/library/core/tests/ops/from_residual.rs @@ -0,0 +1,26 @@ +//! Regression test that Option and ControlFlow can have downstream FromResidual impls. +//! cc https://github.com/rust-lang/rust/issues/99940, +//! This does NOT test that issue in general; Option and ControlFlow's FromResidual +//! impls in core were changed to not be affected by that issue. + +use core::ops::{ControlFlow, FromResidual}; + +struct Local; + +impl FromResidual for Option { + fn from_residual(_: Local) -> Option { + unimplemented!() + } +} + +impl FromResidual for ControlFlow { + fn from_residual(_: Local) -> ControlFlow { + unimplemented!() + } +} + +impl FromResidual for Result { + fn from_residual(_: Local) -> Result { + unimplemented!() + } +} diff --git a/library/core/tests/pin.rs b/library/core/tests/pin.rs index 6f617c8d0c297..7a6af46a74323 100644 --- a/library/core/tests/pin.rs +++ b/library/core/tests/pin.rs @@ -29,3 +29,49 @@ fn pin_const() { pin_mut_const(); } + +#[allow(unused)] +mod pin_coerce_unsized { + use core::cell::{Cell, RefCell, UnsafeCell}; + use core::pin::Pin; + use core::ptr::NonNull; + + pub trait MyTrait {} + impl MyTrait for String {} + + // These Pins should continue to compile. + // Do note that these instances of Pin types cannot be used + // meaningfully because all methods require a Deref/DerefMut + // bounds on the pointer type and Cell, RefCell and UnsafeCell + // do not implement Deref/DerefMut. + + pub fn cell(arg: Pin>>) -> Pin>> { + arg + } + pub fn ref_cell(arg: Pin>>) -> Pin>> { + arg + } + pub fn unsafe_cell(arg: Pin>>) -> Pin>> { + arg + } + + // These sensible Pin coercions are possible. + pub fn pin_mut_ref(arg: Pin<&mut String>) -> Pin<&mut dyn MyTrait> { + arg + } + pub fn pin_ref(arg: Pin<&String>) -> Pin<&dyn MyTrait> { + arg + } + pub fn pin_ptr(arg: Pin<*const String>) -> Pin<*const dyn MyTrait> { + arg + } + pub fn pin_ptr_mut(arg: Pin<*mut String>) -> Pin<*mut dyn MyTrait> { + arg + } + pub fn pin_non_null(arg: Pin>) -> Pin> { + arg + } + pub fn nesting_pins(arg: Pin>) -> Pin> { + arg + } +} diff --git a/library/core/tests/pin_macro.rs b/library/core/tests/pin_macro.rs index 57485ef3974cc..36c6972515a96 100644 --- a/library/core/tests/pin_macro.rs +++ b/library/core/tests/pin_macro.rs @@ -1,10 +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 e3830165eda61..78d1b137e63f5 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -810,9 +810,12 @@ fn ptr_metadata() { assert_ne!(address_1, address_2); // Different erased type => different vtable pointer assert_ne!(address_2, address_3); - // Same erased type and same trait => same vtable pointer - assert_eq!(address_3, address_4); - assert_eq!(address_3, address_5); + // Same erased type and same trait => same vtable pointer. + // This is *not guaranteed*, so we skip it in Miri. + if !cfg!(miri) { + assert_eq!(address_3, address_4); + assert_eq!(address_3, address_5); + } } } @@ -1050,7 +1053,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)?)) } 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 4cbbabb672ba0..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] @@ -1856,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..8f6bf0565fc35 100644 --- a/library/core/tests/waker.rs +++ b/library/core/tests/waker.rs @@ -4,14 +4,13 @@ use std::task::{RawWaker, RawWakerVTable, Waker}; #[test] fn test_waker_getters() { let raw_waker = RawWaker::new(ptr::without_provenance_mut(42usize), &WAKER_VTABLE); - assert_eq!(raw_waker.data() as usize, 42); - assert!(ptr::eq(raw_waker.vtable(), &WAKER_VTABLE)); - let waker = unsafe { Waker::from_raw(raw_waker) }; + assert_eq!(waker.data() as usize, 42); + assert!(ptr::eq(waker.vtable(), &WAKER_VTABLE)); + let waker2 = waker.clone(); - let raw_waker2 = waker2.as_raw(); - assert_eq!(raw_waker2.data() as usize, 43); - assert!(ptr::eq(raw_waker2.vtable(), &WAKER_VTABLE)); + assert_eq!(waker2.data() as usize, 43); + assert!(ptr::eq(waker2.vtable(), &WAKER_VTABLE)); } static WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( @@ -20,3 +19,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 14ba4af2bb575..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)] -#![cfg_attr(bootstrap, 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 f830808d19648..6d1f9764efbfd 100644 --- a/library/panic_unwind/Cargo.toml +++ b/library/panic_unwind/Cargo.toml @@ -20,3 +20,10 @@ 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 } + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + # #[cfg(bootstrap)] rtems + 'cfg(target_os, values("rtems"))', +] 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 77abb9125f651..4552fb68d26d5 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)] -#![cfg_attr(bootstrap, feature(c_unwind))] // `real_imp` is unused with Miri, so silence warnings. #![cfg_attr(miri, allow(dead_code))] #![allow(internal_features)] @@ -49,7 +48,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf")), + all(target_family = "unix", not(any(target_os = "espidf", target_os = "rtems"))), all(target_vendor = "fortanix", target_env = "sgx"), target_family = "wasm", ))] { diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index 04c3d3bf9c359..070c11926f6e0 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -157,7 +157,7 @@ mod imp { // going to be cross-lang LTOed anyway. However, using expose is shorter and // requires less unsafe. let addr: usize = ptr.expose_provenance(); - let image_base = unsafe { addr_of!(__ImageBase) }.addr(); + let image_base = addr_of!(__ImageBase).addr(); let offset: usize = addr - image_base; Self(offset as u32) } @@ -250,7 +250,7 @@ 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 { - pVFTable: unsafe { addr_of!(TYPE_INFO_VTABLE) } as *const _, + 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/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/simd/ptr/const_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs index cbffbc564cfed..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; 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 6bc6ca3ac42dc..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; 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/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 149767bf70521..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; @@ -129,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 9fb79eabd0556..74a41451825ff 100644 --- a/library/proc_macro/src/bridge/fxhash.rs +++ b/library/proc_macro/src/bridge/fxhash.rs @@ -5,8 +5,7 @@ //! on the `rustc_hash` crate. use std::collections::HashMap; -use std::hash::BuildHasherDefault; -use std::hash::Hasher; +use std::hash::{BuildHasherDefault, Hasher}; use std::ops::BitXor; /// Type alias for a hashmap using the `fx` hash algorithm. 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/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/lib.rs b/library/proc_macro/src/lib.rs index 581d7e3efe373..72b53c60f7439 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -28,7 +28,6 @@ #![feature(decl_macro)] #![feature(maybe_uninit_write_slice)] #![feature(negative_impls)] -#![feature(new_uninit)] #![feature(panic_can_unwind)] #![feature(restricted_std)] #![feature(rustc_attrs)] @@ -37,6 +36,7 @@ #![recursion_limit = "256"] #![allow(internal_features)] #![deny(ffi_unwind_calls)] +#![warn(rustdoc::unescaped_backticks)] #[unstable(feature = "proc_macro_internals", issue = "27812")] #[doc(hidden)] @@ -45,16 +45,17 @@ pub mod bridge; mod diagnostic; mod escape; -#[unstable(feature = "proc_macro_diagnostic", issue = "54140")] -pub use diagnostic::{Diagnostic, Level, MultiSpan}; - -use crate::escape::{escape_bytes, EscapeOptions}; use std::ffi::CStr; use std::ops::{Range, RangeBounds}; 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. /// @@ -1544,10 +1545,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/profiler_builtins/build.rs b/library/profiler_builtins/build.rs index 9d1c1ba305bc5..dd85239fa8cfd 100644 --- a/library/profiler_builtins/build.rs +++ b/library/profiler_builtins/build.rs @@ -1,14 +1,15 @@ //! Compiles the profiler part of the `compiler-rt` library. //! -//! See the build.rs for libcompiler_builtins crate for details. +//! Loosely based on: +//! - LLVM's `compiler-rt/lib/profile/CMakeLists.txt` +//! - . use std::env; -use std::path::Path; +use std::path::PathBuf; fn main() { - println!("cargo:rerun-if-env-changed=LLVM_PROFILER_RT_LIB"); - if let Ok(rt) = env::var("LLVM_PROFILER_RT_LIB") { - println!("cargo:rustc-link-lib=static:+verbatim={rt}"); + if let Ok(rt) = tracked_env_var("LLVM_PROFILER_RT_LIB") { + println!("cargo::rustc-link-lib=static:+verbatim={rt}"); return; } @@ -16,13 +17,13 @@ fn main() { let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("CARGO_CFG_TARGET_ENV was not set"); let cfg = &mut cc::Build::new(); - // FIXME: `rerun-if-changed` directives are not currently emitted and the build script - // will not rerun on changes in these source files or headers included into them. - let mut profile_sources = vec![ + let profile_sources = vec![ + // tidy-alphabetical-start "GCDAProfiling.c", "InstrProfiling.c", "InstrProfilingBuffer.c", "InstrProfilingFile.c", + "InstrProfilingInternal.c", "InstrProfilingMerge.c", "InstrProfilingMergeFile.c", "InstrProfilingNameVar.c", @@ -37,15 +38,13 @@ fn main() { "InstrProfilingValue.c", "InstrProfilingVersionVar.c", "InstrProfilingWriter.c", - // These files were added in LLVM 11. - "InstrProfilingInternal.c", - "InstrProfilingBiasVar.c", + "WindowsMMap.c", + // tidy-alphabetical-end ]; if target_env == "msvc" { // Don't pull in extra libraries on MSVC cfg.flag("/Zl"); - profile_sources.push("WindowsMMap.c"); cfg.define("strdup", Some("_strdup")); cfg.define("open", Some("_open")); cfg.define("fdopen", Some("_fdopen")); @@ -60,8 +59,6 @@ fn main() { if target_os != "windows" { cfg.flag("-fvisibility=hidden"); cfg.define("COMPILER_RT_HAS_UNAME", Some("1")); - } else { - profile_sources.push("WindowsMMap.c"); } } @@ -79,19 +76,34 @@ fn main() { cfg.define("COMPILER_RT_HAS_ATOMICS", Some("1")); } - // Note that this should exist if we're going to run (otherwise we just - // don't build profiler builtins at all). - let root = Path::new("../../src/llvm-project/compiler-rt"); + // Get the LLVM `compiler-rt` directory from bootstrap. + let root = PathBuf::from(tracked_env_var_or_fallback( + "RUST_COMPILER_RT_FOR_PROFILER", + "../../src/llvm-project/compiler-rt", + )); let src_root = root.join("lib").join("profile"); - for src in profile_sources { - let path = src_root.join(src); - if path.exists() { - cfg.file(path); - } + assert!(src_root.exists(), "profiler runtime source directory not found: {src_root:?}"); + println!("cargo::rerun-if-changed={}", src_root.display()); + for file in profile_sources { + cfg.file(src_root.join(file)); } - cfg.include(root.join("include")); + let include = root.join("include"); + println!("cargo::rerun-if-changed={}", include.display()); + cfg.include(include); + cfg.warnings(false); cfg.compile("profiler-rt"); } + +fn tracked_env_var(key: &str) -> Result { + println!("cargo::rerun-if-env-changed={key}"); + env::var(key) +} +fn tracked_env_var_or_fallback(key: &str, fallback: &str) -> String { + tracked_env_var(key).unwrap_or_else(|_| { + println!("cargo::warning={key} was not set; falling back to {fallback:?}"); + fallback.to_owned() + }) +} 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 b991b1cf22dd8..e20fe9feff114 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,11 +17,15 @@ 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.123" } 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.24", features = ['rustc-dep-of-std'] } @@ -31,13 +35,30 @@ miniz_oxide = { version = "0.7.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.156", 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.36.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.36.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', +] } + +[target.'cfg(windows)'.dependencies.windows-targets] +path = "../windows_targets" [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } @@ -47,23 +68,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.4.0", 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 +98,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"] @@ -89,7 +119,7 @@ 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 = [] +windows_raw_dylib = ["windows-targets/windows_raw_dylib"] [package.metadata.fortanix-sgx] # Maximum possible number of threads when testing @@ -97,6 +127,11 @@ 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" @@ -111,4 +146,6 @@ check-cfg = [ # and to the `backtrace` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg 'cfg(feature, values(any()))', + # #[cfg(bootstrap)] rtems + 'cfg(target_os, values("rtems"))', ] 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 c542ba81eedc1..ba1eece46f3ce 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -11,6 +11,7 @@ fn main() { .expect("CARGO_CFG_TARGET_POINTER_WIDTH was not set") .parse() .unwrap(); + let is_miri = env::var_os("CARGO_CFG_MIRI").is_some(); println!("cargo:rustc-check-cfg=cfg(netbsd10)"); if target_os == "netbsd" && env::var("RUSTC_STD_NETBSD10").is_ok() { @@ -52,6 +53,7 @@ fn main() { || target_os == "uefi" || target_os == "teeos" || target_os == "zkvm" + || target_os == "rtems" // See src/bootstrap/src/core/build_steps/synthetic_targets.rs || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok() @@ -85,7 +87,14 @@ fn main() { println!("cargo:rustc-check-cfg=cfg(reliable_f16)"); println!("cargo:rustc-check-cfg=cfg(reliable_f128)"); + // This is a step beyond only having the types and basic functions available. Math functions + // aren't consistently available or correct. + println!("cargo:rustc-check-cfg=cfg(reliable_f16_math)"); + println!("cargo:rustc-check-cfg=cfg(reliable_f128_math)"); + let has_reliable_f16 = match (target_arch.as_str(), target_os.as_str()) { + // We can always enable these in Miri as that is not affected by codegen bugs. + _ if is_miri => true, // Selection failure until recent LLVM // FIXME(llvm19): can probably be removed at the version bump ("loongarch64", _) => false, @@ -94,10 +103,10 @@ fn main() { // Unsupported ("arm64ec", _) => false, // MinGW ABI bugs - ("x86", "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, + ("x86_64", "windows") => false, + // Apple has a special ABI for `f16` that we do not yet support + // FIXME(builtins): fixed by + ("x86" | "x86_64", _) if target_vendor == "apple" => false, // Missing `__gnu_h2f_ieee` and `__gnu_f2h_ieee` ("powerpc" | "powerpc64", _) => false, // Missing `__gnu_h2f_ieee` and `__gnu_f2h_ieee` @@ -113,6 +122,8 @@ fn main() { }; let has_reliable_f128 = match (target_arch.as_str(), target_os.as_str()) { + // We can always enable these in Miri as that is not affected by codegen bugs. + _ if is_miri => true, // Unsupported ("arm64ec", _) => false, // ABI and precision bugs @@ -122,16 +133,54 @@ fn main() { ("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, }; + // Configure platforms that have reliable basics but may have unreliable math. + + // LLVM is currently adding missing routines, + let has_reliable_f16_math = has_reliable_f16 + && match (target_arch.as_str(), target_os.as_str()) { + // FIXME: Disabled on Miri as the intrinsics are not implemented yet. + _ if is_miri => false, + // x86 has a crash for `powi`: + ("x86" | "x86_64", _) => false, + // Assume that working `f16` means working `f16` math for most platforms, since + // operations just go through `f32`. + _ => true, + }; + + let has_reliable_f128_math = has_reliable_f128 + && match (target_arch.as_str(), target_os.as_str()) { + // FIXME: Disabled on Miri as the intrinsics are not implemented yet. + _ if is_miri => false, + // LLVM lowers `fp128` math to `long double` symbols even on platforms where + // `long double` is not IEEE binary128. See + // . + // + // This rules out anything that doesn't have `long double` = `binary128`; <= 32 bits + // (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86` + // (ld is 80-bit extended precision). + ("x86_64", _) => false, + (_, "linux") if target_pointer_width == 64 => true, + _ => false, + }; + if has_reliable_f16 { println!("cargo:rustc-cfg=reliable_f16"); } if has_reliable_f128 { println!("cargo:rustc-cfg=reliable_f128"); } + if has_reliable_f16_math { + println!("cargo:rustc-cfg=reliable_f16_math"); + } + if has_reliable_f128_math { + println!("cargo:rustc-cfg=reliable_f128_math"); + } } diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index dc4924cdf581d..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)] 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 4d376753cb6d2..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::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 diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 1f6a3e904795a..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}; 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 fc9b8cfd46d65..28916130b1900 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`]. /// @@ -200,13 +198,12 @@ impl fmt::Debug for VarsOs { /// /// # Errors /// -/// This function will return an error if the environment variable isn't set. +/// Returns [`VarError::NotPresent`] if: +/// - The variable is not set. +/// - The variable's name contains an equal sign or NUL (`'='` or `'\0'`). /// -/// This function may return an error if the environment variable's name contains -/// the equal sign character (`=`) or the NUL character. -/// -/// This function will return an error if the environment variable's value is -/// not valid Unicode. If this is not desired, consider using [`var_os`]. +/// Returns [`VarError::NotUnicode`] if the variable's value is not valid +/// Unicode. If this is not desired, consider using [`var_os`]. /// /// # Examples /// @@ -357,7 +354,9 @@ impl Error for VarError { /// } /// assert_eq!(env::var(key), Ok("VALUE".to_string())); /// ``` -#[rustc_deprecated_safe_2024] +#[rustc_deprecated_safe_2024( + audit_that = "the environment access only happens in single-threaded code" +)] #[stable(feature = "env", since = "1.0.0")] pub unsafe fn set_var, V: AsRef>(key: K, value: V) { let (key, value) = (key.as_ref(), value.as_ref()); @@ -421,7 +420,9 @@ pub unsafe fn set_var, V: AsRef>(key: K, value: V) { /// } /// assert!(env::var(key).is_err()); /// ``` -#[rustc_deprecated_safe_2024] +#[rustc_deprecated_safe_2024( + audit_that = "the environment access only happens in single-threaded code" +)] #[stable(feature = "env", since = "1.0.0")] pub unsafe fn remove_var>(key: K) { let key = key.as_ref(); diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 87aad8f764bd0..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) @@ -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 0591c6f517b44..b436fe9929c36 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -7,30 +7,185 @@ #[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))] +use crate::sys::cmath; + #[cfg(not(test))] impl f128 { - /// Raises a number to an integer power. + /// Returns the largest integer less than or equal to `self`. /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. + /// This function always returns the precise result. /// - /// # Unspecified precision + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` #[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 powi(self, n: i32) -> f128 { - unsafe { intrinsics::powif128(self, n) } + pub fn floor(self) -> f128 { + unsafe { intrinsics::floorf128(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.01_f128; + /// let g = 4.0_f128; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[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 ceil(self) -> f128 { + unsafe { intrinsics::ceilf128(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = -3.7_f128; + /// let i = 3.5_f128; + /// let j = 4.5_f128; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[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 round(self) -> f128 { + unsafe { intrinsics::roundf128(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = 3.5_f128; + /// let i = 4.5_f128; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[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 round_ties_even(self) -> f128 { + unsafe { intrinsics::rintf128(self) } + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[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 trunc(self) -> f128 { + unsafe { intrinsics::truncf128(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 3.6_f128; + /// let y = -3.6_f128; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f128::EPSILON); + /// assert!(abs_difference_y <= f128::EPSILON); + /// # } + /// ``` + #[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 fract(self) -> f128 { + self - self.trunc() } /// Computes the absolute value of `self`. @@ -41,7 +196,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128)] { // FIXME(f16_f128): reliable_f128 + /// # #[cfg(reliable_f128)] { /// /// let x = 3.5_f128; /// let y = -3.5_f128; @@ -53,7 +208,6 @@ impl f128 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] @@ -62,4 +216,1134 @@ impl f128 { // We don't do this now because LLVM has lowering bugs for f128 math. Self::from_bits(self.to_bits() & !(1 << 127)) } + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `INFINITY` + /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` + /// - NaN if the number is NaN + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.5_f128; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f128::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f128::NAN.signum().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 signum(self) -> f128 { + if self.is_nan() { Self::NAN } else { 1.0_f128.copysign(self) } + } + + /// Returns a number composed of the magnitude of `self` and the sign of + /// `sign`. + /// + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`. + /// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is + /// returned. + /// + /// If `sign` is a NaN, then this operation will still carry over its sign into the result. 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 `copysign` with `sign` being a NaN might produce an unexpected or non-portable + /// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more + /// info. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.5_f128; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f128); + /// assert_eq!(f.copysign(-0.42), -3.5_f128); + /// assert_eq!((-f).copysign(0.42), 3.5_f128); + /// assert_eq!((-f).copysign(-0.42), -3.5_f128); + /// + /// assert!(f128::NAN.copysign(1.0).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 copysign(self, sign: f128) -> f128 { + unsafe { intrinsics::copysignf128(self, sign) } + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let m = 10.0_f128; + /// let x = 4.0_f128; + /// let b = 60.0_f128; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f128 + f128::EPSILON; + /// let one_minus_eps = 1.0_f128 - f128::EPSILON; + /// let minus_one = -1.0_f128; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f128::EPSILON * f128::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[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 mul_add(self, a: f128, b: f128) -> f128 { + unsafe { intrinsics::fmaf128(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[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 div_euclid(self, rhs: f128) -> f128 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f128::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f128) -> f128 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + #[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 powi(self, n: i32) -> f128 { + unsafe { intrinsics::powif128(self, n) } + } + + /// Raises a number to a floating point power. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 powf(self, n: f128) -> f128 { + unsafe { intrinsics::powf128(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let positive = 4.0_f128; + /// let negative = -4.0_f128; + /// let negative_zero = -0.0_f128; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[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 sqrt(self) -> f128 { + unsafe { intrinsics::sqrtf128(self) } + } + + /// Returns `e^(self)`, (the exponential function). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let one = 1.0f128; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 exp(self) -> f128 { + unsafe { intrinsics::expf128(self) } + } + + /// Returns `2^(self)`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 2.0f128; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 exp2(self) -> f128 { + unsafe { intrinsics::exp2f128(self) } + } + + /// Returns the natural logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let one = 1.0f128; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 ln(self) -> f128 { + unsafe { intrinsics::logf128(self) } + } + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// The result might not be correctly rounded owing to implementation details; + /// `self.log2()` can produce more accurate results for base 2, and + /// `self.log10()` can produce more accurate results for base 10. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let five = 5.0f128; + /// + /// // log5(5) - 1 == 0 + /// let abs_difference = (five.log(5.0) - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 log(self, base: f128) -> f128 { + self.ln() / base.ln() + } + + /// Returns the base 2 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let two = 2.0f128; + /// + /// // log2(2) - 1 == 0 + /// let abs_difference = (two.log2() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 log2(self) -> f128 { + unsafe { intrinsics::log2f128(self) } + } + + /// Returns the base 10 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let ten = 10.0f128; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference = (ten.log10() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 log10(self) -> f128 { + unsafe { intrinsics::log10f128(self) } + } + + /// Returns the cube root of a number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// + /// This function currently corresponds to the `cbrtf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 8.0f128; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 cbrt(self) -> f128 { + unsafe { cmath::cbrtf128(self) } + } + + /// Compute the distance between the origin and a point (`x`, `y`) on the + /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a + /// right-angle triangle with other sides having length `x.abs()` and + /// `y.abs()`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// + /// This function currently corresponds to the `hypotf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0f128; + /// let y = 3.0f128; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 hypot(self, other: f128) -> f128 { + unsafe { cmath::hypotf128(self, other) } + } + + /// Computes the sine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_2; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 sin(self) -> f128 { + unsafe { intrinsics::sinf128(self) } + } + + /// Computes the cosine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0 * std::f128::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 cos(self) -> f128 { + unsafe { intrinsics::cosf128(self) } + } + + /// Computes the tangent of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanf128` from libc on Unix and + /// Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_4; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 tan(self) -> f128 { + unsafe { cmath::tanf128(self) } + } + + /// Computes the arcsine of a number. Return value is in radians in + /// the range [-pi/2, pi/2] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `asinf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = std::f128::consts::FRAC_PI_2; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - std::f128::consts::FRAC_PI_2).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsin")] + #[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 asin(self) -> f128 { + unsafe { cmath::asinf128(self) } + } + + /// Computes the arccosine of a number. Return value is in radians in + /// the range [0, pi] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `acosf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = std::f128::consts::FRAC_PI_4; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - std::f128::consts::FRAC_PI_4).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccos")] + #[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 acos(self) -> f128 { + unsafe { cmath::acosf128(self) } + } + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atanf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 1.0f128; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctan")] + #[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 atan(self) -> f128 { + unsafe { cmath::atanf128(self) } + } + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. + /// + /// * `x = 0`, `y = 0`: `0` + /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` + /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` + /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atan2f128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// // Positive angles measured counter-clockwise + /// // from positive x axis + /// // -pi/4 radians (45 deg clockwise) + /// let x1 = 3.0f128; + /// let y1 = -3.0f128; + /// + /// // 3pi/4 radians (135 deg counter-clockwise) + /// let x2 = -3.0f128; + /// let y2 = 3.0f128; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f128::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f128::consts::FRAC_PI_4)).abs(); + /// + /// assert!(abs_difference_1 <= f128::EPSILON); + /// assert!(abs_difference_2 <= f128::EPSILON); + /// # } + /// ``` + #[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 atan2(self, other: f128) -> f128 { + unsafe { cmath::atan2f128(self, other) } + } + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `(f128::sin(x), + /// f128::cos(x))`. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_4; + /// let f = x.sin_cos(); + /// + /// let abs_difference_0 = (f.0 - x.sin()).abs(); + /// let abs_difference_1 = (f.1 - x.cos()).abs(); + /// + /// assert!(abs_difference_0 <= f128::EPSILON); + /// assert!(abs_difference_1 <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "sincos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + pub fn sin_cos(self) -> (f128, f128) { + (self.sin(), self.cos()) + } + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `expm1f128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1e-8_f128; + /// + /// // for very small x, e^x is approximately 1 + x + x^2 / 2 + /// let approx = x + x * x / 2.0; + /// let abs_difference = (x.exp_m1() - approx).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// # } + /// ``` + #[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 exp_m1(self) -> f128 { + unsafe { cmath::expm1f128(self) } + } + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `log1pf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1e-8_f128; + /// + /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 + /// let approx = x - x * x / 2.0; + /// let abs_difference = (x.ln_1p() - approx).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// # } + /// ``` + #[inline] + #[doc(alias = "log1p")] + #[must_use = "method returns a new number and does not mutate the original value"] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + pub fn ln_1p(self) -> f128 { + unsafe { cmath::log1pf128(self) } + } + + /// Hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `sinhf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// + /// let f = x.sinh(); + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// let g = ((e * e) - 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 sinh(self) -> f128 { + unsafe { cmath::sinhf128(self) } + } + + /// Hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `coshf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// let f = x.cosh(); + /// // Solving cosh() at 1 gives this result + /// let g = ((e * e) + 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// // Same result + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 cosh(self) -> f128 { + unsafe { cmath::coshf128(self) } + } + + /// Hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanhf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// + /// let f = x.tanh(); + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 tanh(self) -> f128 { + unsafe { cmath::tanhf128(self) } + } + + /// Inverse hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1.0f128; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsinh")] + #[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 asinh(self) -> f128 { + let ax = self.abs(); + let ix = 1.0 / ax; + (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + } + + /// Inverse hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1.0f128; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccosh")] + #[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 acosh(self) -> f128 { + if self < 1.0 { + Self::NAN + } else { + (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() + } + } + + /// Inverse hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference <= 1e-5); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctanh")] + #[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 atanh(self) -> f128 { + 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() + } + + /// Gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tgammaf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 5.0f128; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 gamma(self) -> f128 { + unsafe { cmath::tgammaf128(self) } + } + + /// Natural logarithm of the absolute value of the gamma function + /// + /// The integer part of the tuple indicates the sign of the gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `lgammaf128_r` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0f128; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[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 ln_gamma(self) -> (f128, i32) { + let mut signgamp: i32 = 0; + let x = unsafe { cmath::lgammaf128_r(self, &mut signgamp) }; + (x, signgamp) + } } diff --git a/library/std/src/f128/tests.rs b/library/std/src/f128/tests.rs index 0b3e485b0e735..7051c051bf723 100644 --- a/library/std/src/f128/tests.rs +++ b/library/std/src/f128/tests.rs @@ -1,10 +1,23 @@ -#![cfg(not(bootstrap))] // 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; -use crate::num::*; +use crate::num::{FpCategory as Fp, *}; + +// Note these tolerances make sense around zero, but not for more extreme exponents. + +/// For operations that are near exact, usually not involving math of different +/// signs. +const TOL_PRECISE: f128 = 1e-28; + +/// Default tolerances. Works for values that should be near precise but not exact. Roughly +/// the precision carried by `100 * 100`. +const TOL: f128 = 1e-12; + +/// Tolerances for math that is allowed to be imprecise, usually due to multiple chained +/// operations. +#[cfg(reliable_f128_math)] +const TOL_IMPR: f128 = 1e-10; /// Smallest number const TINY_BITS: u128 = 0x1; @@ -43,7 +56,33 @@ fn test_num_f128() { test_num(10f128, 2f128); } -// FIXME(f16_f128): add min and max tests when available +#[test] +#[cfg(reliable_f128_math)] +fn test_min_nan() { + assert_eq!(f128::NAN.min(2.0), 2.0); + assert_eq!(2.0f128.min(f128::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_max_nan() { + assert_eq!(f128::NAN.max(2.0), 2.0); + assert_eq!(2.0f128.max(f128::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_minimum() { + assert!(f128::NAN.minimum(2.0).is_nan()); + assert!(2.0f128.minimum(f128::NAN).is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_maximum() { + assert!(f128::NAN.maximum(2.0).is_nan()); + assert!(2.0f128.maximum(f128::NAN).is_nan()); +} #[test] fn test_nan() { @@ -193,9 +232,100 @@ fn test_classify() { assert_eq!(1e-4932f128.classify(), Fp::Subnormal); } -// FIXME(f16_f128): add missing math functions when available +#[test] +#[cfg(reliable_f128_math)] +fn test_floor() { + assert_approx_eq!(1.0f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.floor(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).floor(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).floor(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).floor(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ceil() { + assert_approx_eq!(1.0f128.ceil(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.ceil(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).ceil(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).ceil(), -1.0f128, TOL_PRECISE); +} #[test] +#[cfg(reliable_f128_math)] +fn test_round() { + assert_approx_eq!(2.5f128.round(), 3.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round_ties_even(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round_ties_even(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round_ties_even(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round_ties_even(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_trunc() { + assert_approx_eq!(1.0f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.trunc(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).trunc(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).trunc(), -1.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_fract() { + assert_approx_eq!(1.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.fract(), 0.3f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.fract(), 0.5f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.fract(), 0.7f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).fract(), -0.3f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).fract(), -0.5f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).fract(), -0.7f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] fn test_abs() { assert_eq!(f128::INFINITY.abs(), f128::INFINITY); assert_eq!(1f128.abs(), 1f128); @@ -295,6 +425,24 @@ fn test_next_down() { } #[test] +#[cfg(reliable_f128_math)] +fn test_mul_add() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(12.3f128.mul_add(4.5, 6.7), 62.05, TOL_PRECISE); + assert_approx_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.65, TOL_PRECISE); + assert_approx_eq!(0.0f128.mul_add(8.9, 1.2), 1.2, TOL_PRECISE); + assert_approx_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6, TOL_PRECISE); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f128.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] fn test_recip() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; @@ -303,11 +451,161 @@ fn test_recip() { assert_eq!(2.0f128.recip(), 0.5); assert_eq!((-0.4f128).recip(), -2.5); assert_eq!(0.0f128.recip(), inf); + assert_approx_eq!( + f128::MAX.recip(), + 8.40525785778023376565669454330438228902076605e-4933, + 1e-4900 + ); assert!(nan.recip().is_nan()); assert_eq!(inf.recip(), 0.0); assert_eq!(neg_inf.recip(), 0.0); } +// Many math functions allow for less accurate results, so the next tolerance up is used + +#[test] +#[cfg(reliable_f128_math)] +fn test_powi() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powi(1), 1.0); + assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL); + assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL); + assert_eq!(8.3f128.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_powf() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powf(1.0), 1.0); + assert_approx_eq!(3.4f128.powf(4.5), 246.40818323761892815995637964326426756, TOL_IMPR); + assert_approx_eq!(2.7f128.powf(-3.2), 0.041652009108526178281070304373500889273, TOL_IMPR); + assert_approx_eq!((-3.1f128).powf(2.0), 9.6100000000000005506706202140776519387, TOL_IMPR); + assert_approx_eq!(5.9f128.powf(-2.0), 0.028727377190462507313100483690639638451, TOL_IMPR); + assert_eq!(8.3f128.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_sqrt_domain() { + assert!(f128::NAN.sqrt().is_nan()); + assert!(f128::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f128).sqrt().is_nan()); + assert_eq!((-0.0f128).sqrt(), -0.0); + assert_eq!(0.0f128.sqrt(), 0.0); + assert_eq!(1.0f128.sqrt(), 1.0); + assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f128.exp()); + assert_approx_eq!(consts::E, 1.0f128.exp(), TOL); + assert_approx_eq!(148.41315910257660342111558004055227962348775, 5.0f128.exp(), TOL); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f128.exp2()); + assert_eq!(1.0, 0.0f128.exp2()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ln() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(1.0f128.exp().ln(), 1.0, TOL); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f128).ln().is_nan()); + assert_eq!((-0.0f128).ln(), neg_inf); + assert_eq!(0.0f128.ln(), neg_inf); + assert_approx_eq!(4.0f128.ln(), 1.3862943611198906188344642429163531366, TOL); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log(10.0), 1.0); + assert_approx_eq!(2.3f128.log(3.5), 0.66485771361478710036766645911922010272, TOL); + assert_eq!(1.0f128.exp().log(1.0f128.exp()), 1.0); + assert!(1.0f128.log(1.0).is_nan()); + assert!(1.0f128.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f128).log(0.1).is_nan()); + assert_eq!((-0.0f128).log(2.0), neg_inf); + assert_eq!(0.0f128.log(7.0), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log2() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(10.0f128.log2(), 3.32192809488736234787031942948939017, TOL); + assert_approx_eq!(2.3f128.log2(), 1.2016338611696504130002982471978765921, TOL); + assert_approx_eq!(1.0f128.exp().log2(), 1.4426950408889634073599246810018921381, TOL); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f128).log2().is_nan()); + assert_eq!((-0.0f128).log2(), neg_inf); + assert_eq!(0.0f128.log2(), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log10() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log10(), 1.0); + assert_approx_eq!(2.3f128.log10(), 0.36172783601759284532595218865859309898, TOL); + assert_approx_eq!(1.0f128.exp().log10(), 0.43429448190325182765112891891660508222, TOL); + assert_eq!(1.0f128.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f128).log10().is_nan()); + assert_eq!((-0.0f128).log10(), neg_inf); + assert_eq!(0.0f128.log10(), neg_inf); +} + #[test] fn test_to_degrees() { let pi: f128 = consts::PI; @@ -315,8 +613,8 @@ fn test_to_degrees() { 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_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL); assert!(nan.to_degrees().is_nan()); assert_eq!(inf.to_degrees(), inf); assert_eq!(neg_inf.to_degrees(), neg_inf); @@ -330,19 +628,122 @@ fn test_to_radians() { 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); + assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL); + assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL); // 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_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE); assert!(nan.to_radians().is_nan()); assert_eq!(inf.to_radians(), inf); assert_eq!(neg_inf.to_radians(), neg_inf); } +#[test] +#[cfg(reliable_f128_math)] +fn test_asinh() { + // Lower accuracy results are allowed, use increased tolerances + assert_eq!(0.0f128.asinh(), 0.0f128); + assert_eq!((-0.0f128).asinh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f128).asinh().is_sign_negative()); + + // issue 63271 + assert_approx_eq!(2.0f128.asinh(), 1.443635475178810342493276740273105f128, TOL_IMPR); + assert_approx_eq!((-2.0f128).asinh(), -1.443635475178810342493276740273105f128, TOL_IMPR); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!( + (-67452098.07139316f128).asinh(), + -18.720075426274544393985484294000831757220, + TOL_IMPR + ); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.sinh().asinh(), TOL_IMPR); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f128, 1e-15f128.sinh().asinh() * 1e15f128, TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_acosh() { + assert_eq!(1.0f128.acosh(), 0.0f128); + assert!(0.999f128.acosh().is_nan()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f128.acosh(), 1.31695789692481670862504634730796844f128, TOL_IMPR); + assert_approx_eq!(3.0f128.acosh(), 1.76274717403908605046521864995958461f128, TOL_IMPR); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.cosh().acosh(), TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_atanh() { + assert_eq!(0.0f128.atanh(), 0.0f128); + assert_eq!((-0.0f128).atanh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(1.0f128.atanh(), inf); + assert_eq!((-1.0f128).atanh(), neg_inf); + assert!(2f128.atanh().atanh().is_nan()); + assert!((-2f128).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f128.atanh(), 0.54930614433405484569762261846126285f128, TOL_IMPR); + assert_approx_eq!((-0.5f128).atanh(), -0.54930614433405484569762261846126285f128, TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(2.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(3.0f128.gamma(), 2.0f128, TOL_IMPR); + assert_approx_eq!(4.0f128.gamma(), 6.0f128, TOL_IMPR); + assert_approx_eq!(5.0f128.gamma(), 24.0f128, TOL_IMPR); + assert_approx_eq!(0.5f128.gamma(), consts::PI.sqrt(), TOL_IMPR); + assert_approx_eq!((-0.5f128).gamma(), -2.0 * consts::PI.sqrt(), TOL_IMPR); + assert_eq!(0.0f128.gamma(), f128::INFINITY); + assert_eq!((-0.0f128).gamma(), f128::NEG_INFINITY); + assert!((-1.0f128).gamma().is_nan()); + assert!((-2.0f128).gamma().is_nan()); + assert!(f128::NAN.gamma().is_nan()); + assert!(f128::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f128::INFINITY.gamma(), f128::INFINITY); + assert_eq!(1760.9f128.gamma(), f128::INFINITY); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(1.0f128.ln_gamma().1, 1); + assert_approx_eq!(2.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(2.0f128.ln_gamma().1, 1); + assert_approx_eq!(3.0f128.ln_gamma().0, 2.0f128.ln(), TOL_IMPR); + assert_eq!(3.0f128.ln_gamma().1, 1); + assert_approx_eq!((-0.5f128).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_IMPR); + assert_eq!((-0.5f128).ln_gamma().1, -1); +} + #[test] fn test_real_consts() { - // FIXME(f16_f128): add math tests when available use super::consts; let pi: f128 = consts::PI; @@ -353,29 +754,34 @@ fn test_real_consts() { 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()); + + assert_approx_eq!(frac_pi_2, pi / 2f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_3, pi / 3f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_4, pi / 4f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_6, pi / 6f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_8, pi / 8f128, TOL_PRECISE); + assert_approx_eq!(frac_1_pi, 1f128 / pi, TOL_PRECISE); + assert_approx_eq!(frac_2_pi, 2f128 / pi, TOL_PRECISE); + + #[cfg(reliable_f128_math)] + { + 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_2_sqrtpi, 2f128 / pi.sqrt(), TOL_PRECISE); + assert_approx_eq!(sqrt2, 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(log2_e, e.log2(), TOL_PRECISE); + assert_approx_eq!(log10_e, e.log10(), TOL_PRECISE); + assert_approx_eq!(ln_2, 2f128.ln(), TOL_PRECISE); + assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE); + } } #[test] @@ -384,10 +790,10 @@ fn test_float_bits_conv() { 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); + assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25, TOL_PRECISE); // 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 diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index d48518622999a..b2cd5fae9d04a 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -7,30 +7,185 @@ #[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))] +use crate::sys::cmath; + #[cfg(not(test))] impl f16 { - /// Raises a number to an integer power. + /// Returns the largest integer less than or equal to `self`. /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. + /// This function always returns the precise result. /// - /// # Unspecified precision + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` #[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 powi(self, n: i32) -> f16 { - unsafe { intrinsics::powif16(self, n) } + pub fn floor(self) -> f16 { + unsafe { intrinsics::floorf16(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.01_f16; + /// let g = 4.0_f16; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[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 ceil(self) -> f16 { + unsafe { intrinsics::ceilf16(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = -3.7_f16; + /// let i = 3.5_f16; + /// let j = 4.5_f16; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[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 round(self) -> f16 { + unsafe { intrinsics::roundf16(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = 3.5_f16; + /// let i = 4.5_f16; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[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 round_ties_even(self) -> f16 { + unsafe { intrinsics::rintf16(self) } + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[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 trunc(self) -> f16 { + unsafe { intrinsics::truncf16(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 3.6_f16; + /// let y = -3.6_f16; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f16::EPSILON); + /// assert!(abs_difference_y <= f16::EPSILON); + /// # } + /// ``` + #[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 fract(self) -> f16 { + self - self.trunc() } /// Computes the absolute value of `self`. @@ -53,7 +208,6 @@ impl f16 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] @@ -61,4 +215,1132 @@ impl f16 { // FIXME(f16_f128): replace with `intrinsics::fabsf16` when available Self::from_bits(self.to_bits() & !(1 << 15)) } + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `INFINITY` + /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` + /// - NaN if the number is NaN + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.5_f16; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f16::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f16::NAN.signum().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 signum(self) -> f16 { + if self.is_nan() { Self::NAN } else { 1.0_f16.copysign(self) } + } + + /// Returns a number composed of the magnitude of `self` and the sign of + /// `sign`. + /// + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`. + /// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is + /// returned. + /// + /// If `sign` is a NaN, then this operation will still carry over its sign into the result. 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 `copysign` with `sign` being a NaN might produce an unexpected or non-portable + /// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more + /// info. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.5_f16; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f16); + /// assert_eq!(f.copysign(-0.42), -3.5_f16); + /// assert_eq!((-f).copysign(0.42), 3.5_f16); + /// assert_eq!((-f).copysign(-0.42), -3.5_f16); + /// + /// assert!(f16::NAN.copysign(1.0).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 copysign(self, sign: f16) -> f16 { + unsafe { intrinsics::copysignf16(self, sign) } + } + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` *may* be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let m = 10.0_f16; + /// let x = 4.0_f16; + /// let b = 60.0_f16; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f16 + f16::EPSILON; + /// let one_minus_eps = 1.0_f16 - f16::EPSILON; + /// let minus_one = -1.0_f16; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f16::EPSILON * f16::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[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 mul_add(self, a: f16, b: f16) -> f16 { + unsafe { intrinsics::fmaf16(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[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 div_euclid(self, rhs: f16) -> f16 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f16::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f16) -> f16 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + #[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 powi(self, n: i32) -> f16 { + unsafe { intrinsics::powif16(self, n) } + } + + /// Raises a number to a floating point power. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 powf(self, n: f16) -> f16 { + unsafe { intrinsics::powf16(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let positive = 4.0_f16; + /// let negative = -4.0_f16; + /// let negative_zero = -0.0_f16; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[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 sqrt(self) -> f16 { + unsafe { intrinsics::sqrtf16(self) } + } + + /// Returns `e^(self)`, (the exponential function). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let one = 1.0f16; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 exp(self) -> f16 { + unsafe { intrinsics::expf16(self) } + } + + /// Returns `2^(self)`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 2.0f16; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 exp2(self) -> f16 { + unsafe { intrinsics::exp2f16(self) } + } + + /// Returns the natural logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let one = 1.0f16; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 ln(self) -> f16 { + unsafe { intrinsics::logf16(self) } + } + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// The result might not be correctly rounded owing to implementation details; + /// `self.log2()` can produce more accurate results for base 2, and + /// `self.log10()` can produce more accurate results for base 10. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let five = 5.0f16; + /// + /// // log5(5) - 1 == 0 + /// let abs_difference = (five.log(5.0) - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 log(self, base: f16) -> f16 { + self.ln() / base.ln() + } + + /// Returns the base 2 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let two = 2.0f16; + /// + /// // log2(2) - 1 == 0 + /// let abs_difference = (two.log2() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 log2(self) -> f16 { + unsafe { intrinsics::log2f16(self) } + } + + /// Returns the base 10 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let ten = 10.0f16; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference = (ten.log10() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 log10(self) -> f16 { + unsafe { intrinsics::log10f16(self) } + } + + /// Returns the cube root of a number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `cbrtf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 8.0f16; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 cbrt(self) -> f16 { + (unsafe { cmath::cbrtf(self as f32) }) as f16 + } + + /// Compute the distance between the origin and a point (`x`, `y`) on the + /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a + /// right-angle triangle with other sides having length `x.abs()` and + /// `y.abs()`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `hypotf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0f16; + /// let y = 3.0f16; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 hypot(self, other: f16) -> f16 { + (unsafe { cmath::hypotf(self as f32, other as f32) }) as f16 + } + + /// Computes the sine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_2; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 sin(self) -> f16 { + unsafe { intrinsics::sinf16(self) } + } + + /// Computes the cosine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0 * std::f16::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 cos(self) -> f16 { + unsafe { intrinsics::cosf16(self) } + } + + /// Computes the tangent of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanf` from libc on Unix and + /// Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_4; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 tan(self) -> f16 { + (unsafe { cmath::tanf(self as f32) }) as f16 + } + + /// Computes the arcsine of a number. Return value is in radians in + /// the range [-pi/2, pi/2] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `asinf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = std::f16::consts::FRAC_PI_2; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - std::f16::consts::FRAC_PI_2).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsin")] + #[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 asin(self) -> f16 { + (unsafe { cmath::asinf(self as f32) }) as f16 + } + + /// Computes the arccosine of a number. Return value is in radians in + /// the range [0, pi] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `acosf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = std::f16::consts::FRAC_PI_4; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - std::f16::consts::FRAC_PI_4).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccos")] + #[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 acos(self) -> f16 { + (unsafe { cmath::acosf(self as f32) }) as f16 + } + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atanf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 1.0f16; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctan")] + #[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 atan(self) -> f16 { + (unsafe { cmath::atanf(self as f32) }) as f16 + } + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. + /// + /// * `x = 0`, `y = 0`: `0` + /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` + /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` + /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atan2f` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// // Positive angles measured counter-clockwise + /// // from positive x axis + /// // -pi/4 radians (45 deg clockwise) + /// let x1 = 3.0f16; + /// let y1 = -3.0f16; + /// + /// // 3pi/4 radians (135 deg counter-clockwise) + /// let x2 = -3.0f16; + /// let y2 = 3.0f16; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f16::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f16::consts::FRAC_PI_4)).abs(); + /// + /// assert!(abs_difference_1 <= f16::EPSILON); + /// assert!(abs_difference_2 <= f16::EPSILON); + /// # } + /// ``` + #[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 atan2(self, other: f16) -> f16 { + (unsafe { cmath::atan2f(self as f32, other as f32) }) as f16 + } + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `(f16::sin(x), + /// f16::cos(x))`. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_4; + /// let f = x.sin_cos(); + /// + /// let abs_difference_0 = (f.0 - x.sin()).abs(); + /// let abs_difference_1 = (f.1 - x.cos()).abs(); + /// + /// assert!(abs_difference_0 <= f16::EPSILON); + /// assert!(abs_difference_1 <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "sincos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + pub fn sin_cos(self) -> (f16, f16) { + (self.sin(), self.cos()) + } + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `expm1f` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1e-4_f16; + /// + /// // for very small x, e^x is approximately 1 + x + x^2 / 2 + /// let approx = x + x * x / 2.0; + /// let abs_difference = (x.exp_m1() - approx).abs(); + /// + /// assert!(abs_difference < 1e-4); + /// # } + /// ``` + #[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 exp_m1(self) -> f16 { + (unsafe { cmath::expm1f(self as f32) }) as f16 + } + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `log1pf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1e-4_f16; + /// + /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 + /// let approx = x - x * x / 2.0; + /// let abs_difference = (x.ln_1p() - approx).abs(); + /// + /// assert!(abs_difference < 1e-4); + /// # } + /// ``` + #[inline] + #[doc(alias = "log1p")] + #[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 ln_1p(self) -> f16 { + (unsafe { cmath::log1pf(self as f32) }) as f16 + } + + /// Hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `sinhf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// + /// let f = x.sinh(); + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// let g = ((e * e) - 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 sinh(self) -> f16 { + (unsafe { cmath::sinhf(self as f32) }) as f16 + } + + /// Hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `coshf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// let f = x.cosh(); + /// // Solving cosh() at 1 gives this result + /// let g = ((e * e) + 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// // Same result + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 cosh(self) -> f16 { + (unsafe { cmath::coshf(self as f32) }) as f16 + } + + /// Hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanhf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// + /// let f = x.tanh(); + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 tanh(self) -> f16 { + (unsafe { cmath::tanhf(self as f32) }) as f16 + } + + /// Inverse hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1.0f16; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsinh")] + #[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 asinh(self) -> f16 { + let ax = self.abs(); + let ix = 1.0 / ax; + (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + } + + /// Inverse hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1.0f16; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccosh")] + #[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 acosh(self) -> f16 { + if self < 1.0 { + Self::NAN + } else { + (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() + } + } + + /// Inverse hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference <= 0.01); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctanh")] + #[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 atanh(self) -> f16 { + 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() + } + + /// Gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tgammaf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 5.0f16; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 gamma(self) -> f16 { + (unsafe { cmath::tgammaf(self as f32) }) as f16 + } + + /// Natural logarithm of the absolute value of the gamma function + /// + /// The integer part of the tuple indicates the sign of the gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `lgamma_r` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0f16; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[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 ln_gamma(self) -> (f16, i32) { + let mut signgamp: i32 = 0; + let x = (unsafe { cmath::lgammaf_r(self as f32, &mut signgamp) }) as f16; + (x, signgamp) + } } diff --git a/library/std/src/f16/tests.rs b/library/std/src/f16/tests.rs index 26658a0be87bc..684ee3f3855b8 100644 --- a/library/std/src/f16/tests.rs +++ b/library/std/src/f16/tests.rs @@ -1,16 +1,24 @@ -#![cfg(not(bootstrap))] // 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; -use crate::num::*; +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_L2: f16 = 0.01; -// const F16_APPROX_L3: f16 = 0.1; -const F16_APPROX_L4: f16 = 0.5; +/// Tolerance for results on the order of 10.0e-2 +#[allow(unused)] +const TOL_N2: f16 = 0.0001; + +/// Tolerance for results on the order of 10.0e+0 +#[allow(unused)] +const TOL_0: f16 = 0.01; + +/// Tolerance for results on the order of 10.0e+2 +#[allow(unused)] +const TOL_P2: f16 = 0.5; + +/// Tolerance for results on the order of 10.0e+4 +#[allow(unused)] +const TOL_P4: f16 = 10.0; /// Smallest number const TINY_BITS: u16 = 0x1; @@ -49,7 +57,33 @@ fn test_num_f16() { test_num(10f16, 2f16); } -// FIXME(f16_f128): add min and max tests when available +#[test] +#[cfg(reliable_f16_math)] +fn test_min_nan() { + assert_eq!(f16::NAN.min(2.0), 2.0); + assert_eq!(2.0f16.min(f16::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_max_nan() { + assert_eq!(f16::NAN.max(2.0), 2.0); + assert_eq!(2.0f16.max(f16::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_minimum() { + assert!(f16::NAN.minimum(2.0).is_nan()); + assert!(2.0f16.minimum(f16::NAN).is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_maximum() { + assert!(f16::NAN.maximum(2.0).is_nan()); + assert!(2.0f16.maximum(f16::NAN).is_nan()); +} #[test] fn test_nan() { @@ -199,9 +233,100 @@ fn test_classify() { assert_eq!(1e-5f16.classify(), Fp::Subnormal); } -// FIXME(f16_f128): add missing math functions when available +#[test] +#[cfg(reliable_f16_math)] +fn test_floor() { + assert_approx_eq!(1.0f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.floor(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).floor(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).floor(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.5f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).floor(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ceil() { + assert_approx_eq!(1.0f16.ceil(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.5f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.ceil(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).ceil(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).ceil(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_round() { + assert_approx_eq!(2.5f16.round(), 3.0f16, TOL_0); + assert_approx_eq!(1.0f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.0f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round_ties_even(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round_ties_even(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round_ties_even(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round_ties_even(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_trunc() { + assert_approx_eq!(1.0f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.trunc(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).trunc(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).trunc(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_fract() { + assert_approx_eq!(1.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!(1.3f16.fract(), 0.3f16, TOL_0); + assert_approx_eq!(1.5f16.fract(), 0.5f16, TOL_0); + assert_approx_eq!(1.7f16.fract(), 0.7f16, TOL_0); + assert_approx_eq!(0.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.3f16).fract(), -0.3f16, TOL_0); + assert_approx_eq!((-1.5f16).fract(), -0.5f16, TOL_0); + assert_approx_eq!((-1.7f16).fract(), -0.7f16, TOL_0); +} #[test] +#[cfg(reliable_f16_math)] fn test_abs() { assert_eq!(f16::INFINITY.abs(), f16::INFINITY); assert_eq!(1f16.abs(), 1f16); @@ -301,6 +426,24 @@ fn test_next_down() { } #[test] +#[cfg(reliable_f16_math)] +fn test_mul_add() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(12.3f16.mul_add(4.5, 6.7), 62.05, TOL_P2); + assert_approx_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.65, TOL_P2); + assert_approx_eq!(0.0f16.mul_add(8.9, 1.2), 1.2, TOL_0); + assert_approx_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6, TOL_0); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f16.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] fn test_recip() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; @@ -309,11 +452,157 @@ fn test_recip() { assert_eq!(2.0f16.recip(), 0.5); assert_eq!((-0.4f16).recip(), -2.5); assert_eq!(0.0f16.recip(), inf); + assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4); assert!(nan.recip().is_nan()); assert_eq!(inf.recip(), 0.0); assert_eq!(neg_inf.recip(), 0.0); } +#[test] +#[cfg(reliable_f16_math)] +fn test_powi() { + // FIXME(llvm19): LLVM misoptimizes `powi.f16` + // + // let nan: f16 = f16::NAN; + // let inf: f16 = f16::INFINITY; + // let neg_inf: f16 = f16::NEG_INFINITY; + // assert_eq!(1.0f16.powi(1), 1.0); + // assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0); + // assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2); + // assert_eq!(8.3f16.powi(0), 1.0); + // assert!(nan.powi(2).is_nan()); + // assert_eq!(inf.powi(3), inf); + // assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_powf() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(1.0f16.powf(1.0), 1.0); + assert_approx_eq!(3.4f16.powf(4.5), 246.408183, TOL_P2); + assert_approx_eq!(2.7f16.powf(-3.2), 0.041652, TOL_N2); + assert_approx_eq!((-3.1f16).powf(2.0), 9.61, TOL_P2); + assert_approx_eq!(5.9f16.powf(-2.0), 0.028727, TOL_N2); + assert_eq!(8.3f16.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_sqrt_domain() { + assert!(f16::NAN.sqrt().is_nan()); + assert!(f16::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f16).sqrt().is_nan()); + assert_eq!((-0.0f16).sqrt(), -0.0); + assert_eq!(0.0f16.sqrt(), 0.0); + assert_eq!(1.0f16.sqrt(), 1.0); + assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f16.exp()); + assert_approx_eq!(2.718282, 1.0f16.exp(), TOL_0); + assert_approx_eq!(148.413159, 5.0f16.exp(), TOL_0); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f16.exp2()); + assert_eq!(1.0, 0.0f16.exp2()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ln() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(1.0f16.exp().ln(), 1.0, TOL_0); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f16).ln().is_nan()); + assert_eq!((-0.0f16).ln(), neg_inf); + assert_eq!(0.0f16.ln(), neg_inf); + assert_approx_eq!(4.0f16.ln(), 1.386294, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log(10.0), 1.0); + assert_approx_eq!(2.3f16.log(3.5), 0.664858, TOL_0); + assert_eq!(1.0f16.exp().log(1.0f16.exp()), 1.0); + assert!(1.0f16.log(1.0).is_nan()); + assert!(1.0f16.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f16).log(0.1).is_nan()); + assert_eq!((-0.0f16).log(2.0), neg_inf); + assert_eq!(0.0f16.log(7.0), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log2() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(10.0f16.log2(), 3.321928, TOL_0); + assert_approx_eq!(2.3f16.log2(), 1.201634, TOL_0); + assert_approx_eq!(1.0f16.exp().log2(), 1.442695, TOL_0); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f16).log2().is_nan()); + assert_eq!((-0.0f16).log2(), neg_inf); + assert_eq!(0.0f16.log2(), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log10() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log10(), 1.0); + assert_approx_eq!(2.3f16.log10(), 0.361728, TOL_0); + assert_approx_eq!(1.0f16.exp().log10(), 0.434294, TOL_0); + assert_eq!(1.0f16.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f16).log10().is_nan()); + assert_eq!((-0.0f16).log10(), neg_inf); + assert_eq!(0.0f16.log10(), neg_inf); +} + #[test] fn test_to_degrees() { let pi: f16 = consts::PI; @@ -321,8 +610,8 @@ fn test_to_degrees() { 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_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2); assert!(nan.to_degrees().is_nan()); assert_eq!(inf.to_degrees(), inf); assert_eq!(neg_inf.to_degrees(), neg_inf); @@ -336,14 +625,112 @@ fn test_to_radians() { 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_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0); + assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0); + assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0); assert!(nan.to_radians().is_nan()); assert_eq!(inf.to_radians(), inf); assert_eq!(neg_inf.to_radians(), neg_inf); } +#[test] +#[cfg(reliable_f16_math)] +fn test_asinh() { + assert_eq!(0.0f16.asinh(), 0.0f16); + assert_eq!((-0.0f16).asinh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f16).asinh().is_sign_negative()); + // issue 63271 + assert_approx_eq!(2.0f16.asinh(), 1.443635475178810342493276740273105f16, TOL_0); + assert_approx_eq!((-2.0f16).asinh(), -1.443635475178810342493276740273105f16, TOL_0); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-200.0f16).asinh(), -5.991470797049389, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.sinh().asinh(), TOL_0); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f16, 1e-3f16.sinh().asinh() * 1e3f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_acosh() { + assert_eq!(1.0f16.acosh(), 0.0f16); + assert!(0.999f16.acosh().is_nan()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f16.acosh(), 1.31695789692481670862504634730796844f16, TOL_0); + assert_approx_eq!(3.0f16.acosh(), 1.76274717403908605046521864995958461f16, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.cosh().acosh(), TOL_P2); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_atanh() { + assert_eq!(0.0f16.atanh(), 0.0f16); + assert_eq!((-0.0f16).atanh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(1.0f16.atanh(), inf); + assert_eq!((-1.0f16).atanh(), neg_inf); + assert!(2f16.atanh().atanh().is_nan()); + assert!((-2f16).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f16.atanh(), 0.54930614433405484569762261846126285f16, TOL_0); + assert_approx_eq!((-0.5f16).atanh(), -0.54930614433405484569762261846126285f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(2.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(3.0f16.gamma(), 2.0f16, TOL_0); + assert_approx_eq!(4.0f16.gamma(), 6.0f16, TOL_0); + assert_approx_eq!(5.0f16.gamma(), 24.0f16, TOL_0); + assert_approx_eq!(0.5f16.gamma(), consts::PI.sqrt(), TOL_0); + assert_approx_eq!((-0.5f16).gamma(), -2.0 * consts::PI.sqrt(), TOL_0); + assert_eq!(0.0f16.gamma(), f16::INFINITY); + assert_eq!((-0.0f16).gamma(), f16::NEG_INFINITY); + assert!((-1.0f16).gamma().is_nan()); + assert!((-2.0f16).gamma().is_nan()); + assert!(f16::NAN.gamma().is_nan()); + assert!(f16::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f16::INFINITY.gamma(), f16::INFINITY); + assert_eq!(171.71f16.gamma(), f16::INFINITY); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(1.0f16.ln_gamma().1, 1); + assert_approx_eq!(2.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(2.0f16.ln_gamma().1, 1); + assert_approx_eq!(3.0f16.ln_gamma().0, 2.0f16.ln(), TOL_0); + assert_eq!(3.0f16.ln_gamma().1, 1); + assert_approx_eq!((-0.5f16).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_0); + assert_eq!((-0.5f16).ln_gamma().1, -1); +} + #[test] fn test_real_consts() { // FIXME(f16_f128): add math tests when available @@ -357,29 +744,34 @@ fn test_real_consts() { 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()); + + assert_approx_eq!(frac_pi_2, pi / 2f16, TOL_0); + assert_approx_eq!(frac_pi_3, pi / 3f16, TOL_0); + assert_approx_eq!(frac_pi_4, pi / 4f16, TOL_0); + assert_approx_eq!(frac_pi_6, pi / 6f16, TOL_0); + assert_approx_eq!(frac_pi_8, pi / 8f16, TOL_0); + assert_approx_eq!(frac_1_pi, 1f16 / pi, TOL_0); + assert_approx_eq!(frac_2_pi, 2f16 / pi, TOL_0); + + #[cfg(reliable_f16_math)] + { + 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_2_sqrtpi, 2f16 / pi.sqrt(), TOL_0); + assert_approx_eq!(sqrt2, 2f16.sqrt(), TOL_0); + assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt(), TOL_0); + assert_approx_eq!(log2_e, e.log2(), TOL_0); + assert_approx_eq!(log10_e, e.log10(), TOL_0); + assert_approx_eq!(ln_2, 2f16.ln(), TOL_0); + assert_approx_eq!(ln_10, 10f16.ln(), TOL_0); + } } #[test] @@ -388,10 +780,10 @@ fn test_float_bits_conv() { 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); + assert_approx_eq!(f16::from_bits(0x3c00), 1.0, TOL_0); + assert_approx_eq!(f16::from_bits(0x4a40), 12.5, TOL_0); + assert_approx_eq!(f16::from_bits(0x6539), 1337.0, TOL_P4); + assert_approx_eq!(f16::from_bits(0xcb20), -14.25, TOL_0); // Check that NaNs roundtrip their bits regardless of signaling-ness let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 4fc82fec0adbc..cafbe9761da19 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`. @@ -226,11 +226,16 @@ impl f32 { /// Returns a number composed of the magnitude of `self` and the sign of /// `sign`. /// - /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise - /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of - /// `sign` is returned. Note, however, that conserving the sign bit on NaN - /// across arithmetical operations is not generally guaranteed. - /// See [explanation of NaN as a special value](primitive@f32) for more info. + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`. + /// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is + /// returned. + /// + /// If `sign` is a NaN, then this operation will still carry over its sign into the result. 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 `copysign` with `sign` being a NaN might produce an unexpected or non-portable + /// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more + /// info. /// /// # Examples /// @@ -574,7 +579,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 63e65698374c8..3a4c1c120a495 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -1,6 +1,5 @@ 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 diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 1ca2b32e241c9..fba283e3a44bc 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`. @@ -226,11 +226,16 @@ impl f64 { /// Returns a number composed of the magnitude of `self` and the sign of /// `sign`. /// - /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise - /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of - /// `sign` is returned. Note, however, that conserving the sign bit on NaN - /// across arithmetical operations is not generally guaranteed. - /// See [explanation of NaN as a special value](primitive@f32) for more info. + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise equal to `-self`. + /// If `self` is a NaN, then a NaN with the same payload as `self` and the sign bit of `sign` is + /// returned. + /// + /// If `sign` is a NaN, then this operation will still carry over its sign into the result. 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 `copysign` with `sign` being a NaN might produce an unexpected or non-portable + /// result. See the [specification of NaN bit patterns](primitive@f32#nan-bit-patterns) for more + /// info. /// /// # Examples /// @@ -574,7 +579,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f64 { - crate::sys::log2f64(self) + unsafe { intrinsics::log2f64(self) } } /// Returns the base 10 logarithm of the number. diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index d9e17fd601d2d..bac8405f97361 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -1,6 +1,5 @@ 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 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 f9dba08da4c3c..99bea676e1224 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -3,19 +3,19 @@ #[cfg(test)] mod tests; +use core::clone::CloneToUninit; + 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::ptr::addr_of_mut; 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 +115,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, } @@ -854,7 +852,7 @@ impl OsStr { /// Converts an `OsStr` to a [Cow]<[str]>. /// - /// Any non-Unicode sequences are replaced with + /// Any non-UTF-8 sequences are replaced with /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. /// /// [U+FFFD]: crate::char::REPLACEMENT_CHARACTER @@ -1266,6 +1264,16 @@ impl Clone for Box { } } +#[unstable(feature = "clone_to_uninit", issue = "126799")] +unsafe impl CloneToUninit for OsStr { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_to_uninit(&self, dst: *mut Self) { + // SAFETY: we're just a wrapper around a platform-specific Slice + unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) } + } +} + #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Arc { /// Converts an [`OsString`] into an [Arc]<[OsStr]> by moving the [`OsString`] diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs index 5b39b9e34d8c7..67147934b4db3 100644 --- a/library/std/src/ffi/os_str/tests.rs +++ b/library/std/src/ffi/os_str/tests.rs @@ -1,4 +1,6 @@ use super::*; +use crate::mem::MaybeUninit; +use crate::ptr; #[test] fn test_os_string_with_capacity() { @@ -286,3 +288,18 @@ fn slice_surrogate_edge() { assert_eq!(post_crab.slice_encoded_bytes(..4), "🦀"); assert_eq!(post_crab.slice_encoded_bytes(4..), surrogate); } + +#[test] +fn clone_to_uninit() { + let a = OsStr::new("hello.txt"); + + let mut storage = vec![MaybeUninit::::uninit(); size_of_val::(a)]; + unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()) as *mut OsStr) }; + assert_eq!(a.as_encoded_bytes(), unsafe { MaybeUninit::slice_assume_init_ref(&storage) }); + + let mut b: Box = OsStr::new("world.exe").into(); + assert_eq!(size_of_val::(a), size_of_val::(&b)); + assert_ne!(a, &*b); + unsafe { a.clone_to_uninit(ptr::from_mut::(&mut b)) }; + assert_eq!(a, &*b); +} diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 6413b3515ecec..6a0d9f47960ec 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,7 +767,7 @@ fn buffer_capacity_required(mut file: &File) -> Option { #[stable(feature = "rust1", since = "1.0.0")] impl Read for &File { - /// Read some bytes from the file. + /// Reads some bytes from the file. /// /// See [`Read::read`] docs for more info. /// @@ -835,7 +835,7 @@ impl Read for &File { } #[stable(feature = "rust1", since = "1.0.0")] impl Write for &File { - /// Write some bytes from the file. + /// Writes some bytes from the file. /// /// See [`Write::write`] docs for more info. /// @@ -1526,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")] @@ -2005,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 @@ -2044,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 /// @@ -2079,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. @@ -2400,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. @@ -2496,6 +2491,8 @@ pub fn remove_dir>(path: P) -> io::Result<()> { /// /// Consider ignoring the error if validating the removal is not required for your use case. /// +/// [`io::ErrorKind::NotFound`] is only returned if no removal occurs. +/// /// [`fs::remove_file`]: remove_file /// [`fs::remove_dir`]: remove_dir /// @@ -2749,7 +2746,7 @@ impl AsInnerMut for DirBuilder { /// ``` /// /// [`Path::exists`]: crate::path::Path::exists -#[stable(feature = "fs_try_exists", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fs_try_exists", since = "1.81.0")] #[inline] 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 5ca631399aa4a..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) => { @@ -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 || { @@ -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(); diff --git a/library/std/src/hash/random.rs b/library/std/src/hash/random.rs index 0adf91e14ac6e..8ef45172eac40 100644 --- a/library/std/src/hash/random.rs +++ b/library/std/src/hash/random.rs @@ -10,8 +10,7 @@ #[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..cf226bd28d005 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. /// @@ -95,6 +96,42 @@ impl BufReader { } } +impl BufReader { + /// Attempt to look ahead `n` bytes. + /// + /// `n` must be less than `capacity`. + /// + /// ## Examples + /// + /// ```rust + /// #![feature(bufreader_peek)] + /// use std::io::{Read, BufReader}; + /// + /// let mut bytes = &b"oh, hello"[..]; + /// let mut rdr = BufReader::with_capacity(6, &mut bytes); + /// assert_eq!(rdr.peek(2).unwrap(), b"oh"); + /// let mut buf = [0; 4]; + /// rdr.read(&mut buf[..]).unwrap(); + /// assert_eq!(&buf, b"oh, "); + /// assert_eq!(rdr.peek(2).unwrap(), b"he"); + /// let mut s = String::new(); + /// rdr.read_to_string(&mut s).unwrap(); + /// assert_eq!(&s, "hello"); + /// ``` + #[unstable(feature = "bufreader_peek", issue = "128405")] + pub fn peek(&mut self, n: usize) -> io::Result<&[u8]> { + assert!(n <= self.capacity()); + while n > self.buf.buffer().len() { + if self.buf.pos() > 0 { + self.buf.backshift(); + } + self.buf.read_more(&mut self.inner)?; + debug_assert_eq!(self.buf.pos(), 0); + } + Ok(&self.buf.buffer()[..n]) + } +} + impl BufReader { /// Gets a reference to the underlying reader. /// diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 796137c0123e7..ccd67fafb45b4 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -97,6 +97,27 @@ impl Buffer { self.pos = self.pos.saturating_sub(amt); } + /// Read more bytes into the buffer without discarding any of its contents + pub fn read_more(&mut self, mut reader: impl Read) -> io::Result<()> { + let mut buf = BorrowedBuf::from(&mut self.buf[self.pos..]); + let old_init = self.initialized - self.pos; + unsafe { + buf.set_init(old_init); + } + reader.read_buf(buf.unfilled())?; + self.filled += buf.len(); + self.initialized += buf.init_len() - old_init; + Ok(()) + } + + /// Remove bytes that have already been read from the buffer. + pub fn backshift(&mut self) { + self.buf.copy_within(self.pos.., 0); + self.initialized -= self.pos; + self.filled -= self.pos; + self.pos = 0; + } + #[inline] pub fn fill_buf(&mut self, mut reader: impl Read) -> io::Result<&[u8]> { // If we've reached the end of our internal buffer then we need to fetch diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index a8680e9b6ead1..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::{self, ManuallyDrop}; -use crate::ptr; +use crate::{error, fmt, ptr}; /// Wraps a writer and buffers its output. /// 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 ab66deaf31d22..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 { 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 2ed64a40495ef..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; 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 fbb74967df3f1..9d3ade46bd929 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -102,10 +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::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; 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 1345a30361e28..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; @@ -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")] @@ -1266,7 +1262,7 @@ impl<'a> IoSliceMut<'a> { /// buf.advance(3); /// assert_eq!(buf.deref(), [1; 5].as_ref()); /// ``` - #[stable(feature = "io_slice_advance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "io_slice_advance", since = "1.81.0")] #[inline] pub fn advance(&mut self, n: usize) { self.0.advance(n) @@ -1305,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()); /// ``` - #[stable(feature = "io_slice_advance", since = "CURRENT_RUSTC_VERSION")] + #[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. @@ -1406,7 +1402,7 @@ impl<'a> IoSlice<'a> { /// buf.advance(3); /// assert_eq!(buf.deref(), [1; 5].as_ref()); /// ``` - #[stable(feature = "io_slice_advance", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "io_slice_advance", since = "1.81.0")] #[inline] pub fn advance(&mut self, n: usize) { self.0.advance(n) @@ -1444,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()); - #[stable(feature = "io_slice_advance", since = "CURRENT_RUSTC_VERSION")] + #[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. @@ -1531,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 @@ -1630,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 @@ -2247,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`. @@ -2278,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, @@ -2337,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. @@ -2399,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 @@ -3038,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 9aee2bb5e1c5c..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 diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index a2c1c430863ab..24e5a1dfd5c00 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"); } @@ -737,7 +738,7 @@ fn read_buf_full_read() { #[test] // Miri does not support signalling OOM #[cfg_attr(miri, ignore)] -// 64-bit only to be sure the allocator will fail fast on an impossible to satsify size +// 64-bit only to be sure the allocator will fail fast on an impossible to satisfy size #[cfg(target_pointer_width = "64")] fn try_oom_error() { let mut v = Vec::::new(); 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..9f4d244b5479e 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -155,7 +155,7 @@ mod break_keyword {} /// const WORDS: &str = "hello convenience!"; /// ``` /// -/// `const` items looks remarkably similar to `static` items, which introduces some confusion as +/// `const` items look remarkably similar to `static` items, which introduces some confusion as /// to which one should be used at which times. To put it simply, constants are inlined wherever /// they're used, making using them identical to simply replacing the name of the `const` with its /// value. Static variables, on the other hand, point to a single location in memory, which all @@ -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 f0a73a308a4a4..60969af3e8541 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -254,6 +254,7 @@ #![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 @@ -268,14 +269,9 @@ #![cfg_attr(any(windows, target_os = "uefi"), feature(round_char_boundary))] #![cfg_attr(target_family = "wasm", feature(stdarch_wasm_atomic_wait))] #![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] -#![cfg_attr( - all(any(target_arch = "x86_64", target_arch = "x86"), target_os = "uefi"), - feature(stdarch_x86_has_cpuid) -)] // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(c_unwind))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] @@ -293,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)] @@ -301,17 +298,19 @@ #![feature(let_chains)] #![feature(link_cfg)] #![feature(linkage)] -#![feature(min_exhaustive_patterns)] +#![feature(macro_metavar_expr_concat)] #![feature(min_specialization)] #![feature(must_not_suspend)] #![feature(needs_panic_runtime)] #![feature(negative_impls)] #![feature(never_type)] #![feature(no_sanitize)] +#![feature(optimize_attribute)] #![feature(prelude_import)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] #![feature(staged_api)] +#![feature(stmt_expr_attributes)] #![feature(thread_local)] #![feature(try_blocks)] #![feature(type_alias_impl_trait)] @@ -321,6 +320,7 @@ // tidy-alphabetical-start #![feature(c_str_module)] #![feature(char_internals)] +#![feature(clone_to_uninit)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] #![feature(duration_constants)] @@ -341,6 +341,7 @@ #![feature(maybe_uninit_write_slice)] #![feature(panic_can_unwind)] #![feature(panic_internals)] +#![feature(pin_coerce_unsized_trait)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(prelude_2024)] @@ -362,7 +363,7 @@ #![feature(allocator_api)] #![feature(get_mut_unchecked)] #![feature(map_try_insert)] -#![feature(new_uninit)] +#![feature(new_zeroed_alloc)] #![feature(slice_concat_trait)] #![feature(thin_box)] #![feature(try_reserve_kind)] @@ -406,7 +407,6 @@ #![feature(const_ip)] #![feature(const_ipv4)] #![feature(const_ipv6)] -#![feature(const_waker)] #![feature(thread_local_internals)] // tidy-alphabetical-end // @@ -470,24 +470,6 @@ pub mod rt; // The Rust prelude pub mod prelude; -#[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")] @@ -565,6 +547,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")] @@ -587,9 +588,11 @@ pub mod net; pub mod num; pub mod os; pub mod panic; -#[unstable(feature = "core_pattern_types", issue = "none")] +#[unstable(feature = "core_pattern_types", issue = "123646")] pub mod pat; pub mod path; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub mod pipe; pub mod process; pub mod sync; pub mod time; @@ -606,23 +609,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")] @@ -667,35 +670,31 @@ mod panicking; #[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 972b6015932db..1b0d7f3dbf2c9 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: @@ -382,7 +382,7 @@ macro_rules! assert_approx_eq { let diff = (*a - *b).abs(); assert!( diff < $lim, - "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, actual {diff:?})", + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {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 858776f14466a..3b19c743b1e24 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -21,7 +21,8 @@ #![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}; @@ -33,8 +34,7 @@ pub use self::tcp::IntoIncoming; 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 6336354239b02..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. diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 3ad046733a634..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()); 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/darwin/fs.rs b/library/std/src/os/darwin/fs.rs index 2032cca311a15..2d154b214b5f0 100644 --- a/library/std/src/os/darwin/fs.rs +++ b/library/std/src/os/darwin/fs.rs @@ -1,13 +1,12 @@ #![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 super::raw; - /// OS-specific extensions to [`fs::Metadata`]. /// /// [`fs::Metadata`]: crate::fs::Metadata 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 a1f83029d2727..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 } } 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 b31dc06f8dfbd..64f4d97ca95e2 100644 --- a/library/std/src/os/fortanix_sgx/mod.rs +++ b/library/std/src/os/fortanix_sgx/mod.rs @@ -22,20 +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}; - - 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/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/mod.rs b/library/std/src/os/ios/mod.rs index 5e130d77b7bfd..52d592ed95afa 100644 --- a/library/std/src/os/ios/mod.rs +++ b/library/std/src/os/ios/mod.rs @@ -7,7 +7,6 @@ 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; 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/macos/mod.rs b/library/std/src/os/macos/mod.rs index 3638406b1807d..59fe90834c2b4 100644 --- a/library/std/src/os/macos/mod.rs +++ b/library/std/src/os/macos/mod.rs @@ -7,7 +7,6 @@ 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; diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 020a8b324f410..a2496baa63fb1 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -143,6 +143,8 @@ pub mod nto; pub mod openbsd; #[cfg(target_os = "redox")] pub mod redox; +#[cfg(target_os = "rtems")] +pub mod rtems; #[cfg(target_os = "solaris")] pub mod solaris; #[cfg(target_os = "solid_asp3")] 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/rtems/fs.rs b/library/std/src/os/rtems/fs.rs new file mode 100644 index 0000000000000..bec0d41e42d81 --- /dev/null +++ b/library/std/src/os/rtems/fs.rs @@ -0,0 +1,374 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Returns the device ID on which this file resides. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_dev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + + /// Returns the inode number. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ino()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + + /// Returns the file type and mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mode()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + + /// Returns the number of hard links to file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_nlink()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + + /// Returns the user ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_uid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + + /// Returns the group ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_gid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + + /// Returns the device ID that this file represents. Only relevant for special file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_rdev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + + /// Returns the size of the file (if it is a regular file or a symbolic link) in bytes. + /// + /// The size of a symbolic link is the length of the pathname it contains, + /// without a terminating null byte. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_size()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + + /// Returns the last access time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + + /// Returns the last access time of the file, in nanoseconds since [`st_atime`]. + /// + /// [`st_atime`]: Self::st_atime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + + /// Returns the last modification time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + + /// Returns the last modification time of the file, in nanoseconds since [`st_mtime`]. + /// + /// [`st_mtime`]: Self::st_mtime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + + /// Returns the last status change time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + + /// Returns the last status change time of the file, in nanoseconds since [`st_ctime`]. + /// + /// [`st_ctime`]: Self::st_ctime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + + /// Returns the "preferred" block size for efficient filesystem I/O. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blksize()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + + /// Returns the number of blocks allocated to the file, 512-byte units. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::rtems::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blocks()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + 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 { + 0 + } + + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + + fn st_mtime_nsec(&self) -> i64 { + 0 + } + + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + + fn st_ctime_nsec(&self) -> i64 { + 0 + } + + 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 + } +} diff --git a/library/std/src/os/rtems/mod.rs b/library/std/src/os/rtems/mod.rs new file mode 100644 index 0000000000000..7275bfd1765d5 --- /dev/null +++ b/library/std/src/os/rtems/mod.rs @@ -0,0 +1,4 @@ +#![stable(feature = "raw_ext", since = "1.1.0")] +#![forbid(unsafe_op_in_unsafe_fn)] +pub mod fs; +pub(crate) mod raw; diff --git a/library/std/src/os/rtems/raw.rs b/library/std/src/os/rtems/raw.rs new file mode 100644 index 0000000000000..113079cf4abdc --- /dev/null +++ b/library/std/src/os/rtems/raw.rs @@ -0,0 +1,33 @@ +//! rtems 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)] + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = libc::pthread_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blkcnt_t = libc::blkcnt_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blksize_t = libc::blksize_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = libc::dev_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type ino_t = libc::ino_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = libc::mode_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type nlink_t = libc::nlink_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type off_t = libc::off_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type time_t = libc::time_t; 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/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 c6581b9c4c8c8..7d2f0bd4efea7 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -73,6 +73,8 @@ mod platform { pub use crate::os::openbsd::*; #[cfg(target_os = "redox")] pub use crate::os::redox::*; + #[cfg(target_os = "rtems")] + pub use crate::os::rtems::*; #[cfg(target_os = "solaris")] pub use crate::os::solaris::*; #[cfg(target_os = "vita")] diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs index fe8e2be93724e..9b487a6298247 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary.rs @@ -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..9aadd9491169f 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"))] { @@ -111,13 +109,17 @@ pub trait CommandExt: Sealed { /// Schedules a closure to be run just before the `exec` function is /// invoked. /// - /// This method is stable and usable, but it should be unsafe. To fix - /// that, it got deprecated in favor of the unsafe [`pre_exec`]. + /// `before_exec` used to be a safe method, but it needs to be unsafe since the closure may only + /// perform operations that are *async-signal-safe*. Hence it got deprecated in favor of the + /// unsafe [`pre_exec`]. Meanwhile, Rust gained the ability to make an existing safe method + /// fully unsafe in a new edition, which is how `before_exec` became `unsafe`. It still also + /// remains deprecated; `pre_exec` should be used instead. /// /// [`pre_exec`]: CommandExt::pre_exec #[stable(feature = "process_exec", since = "1.15.0")] #[deprecated(since = "1.37.0", note = "should be unsafe, use `pre_exec` instead")] - fn before_exec(&mut self, f: F) -> &mut process::Command + #[rustc_deprecated_safe_2024(audit_that = "the closure is async-signal-safe")] + unsafe fn before_exec(&mut self, f: F) -> &mut process::Command where F: FnMut() -> io::Result<()> + Send + Sync + 'static, { @@ -444,7 +446,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 +477,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 +508,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/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/vxworks/mod.rs b/library/std/src/os/vxworks/mod.rs index 0a7ac641dd3e1..b09aa72f72693 100644 --- a/library/std/src/os/vxworks/mod.rs +++ b/library/std/src/os/vxworks/mod.rs @@ -1,6 +1,7 @@ //! VxWorks-specific definitions #![stable(feature = "raw_ext", since = "1.1.0")] +#![forbid(unsafe_op_in_unsafe_fn)] pub mod fs; pub mod raw; diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 46fc2a50de911..9ec3e387e2ba9 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -2,17 +2,17 @@ //! //! [`std::fs`]: crate::fs -#![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 +169,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 +501,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 +520,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 +537,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 +551,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/mod.rs b/library/std/src/os/wasi/mod.rs index e36b93e60ea1c..33b50c9e53b8f 100644 --- a/library/std/src/os/wasi/mod.rs +++ b/library/std/src/os/wasi/mod.rs @@ -30,7 +30,7 @@ #![cfg_attr(not(target_env = "p2"), stable(feature = "rust1", since = "1.0.0"))] #![cfg_attr(target_env = "p2", unstable(feature = "wasip2", issue = "none"))] -#![deny(unsafe_op_in_unsafe_fn)] +#![forbid(unsafe_op_in_unsafe_fn)] #![doc(cfg(target_os = "wasi"))] pub mod ffi; 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/wasip2/mod.rs b/library/std/src/os/wasip2/mod.rs index 1d44dd72814b8..809a288f20d04 100644 --- a/library/std/src/os/wasip2/mod.rs +++ b/library/std/src/os/wasip2/mod.rs @@ -2,4 +2,5 @@ //! //! This module is currently empty, but will be filled over time as wasi-libc support for WASI Preview 2 is stabilized. +#![forbid(unsafe_op_in_unsafe_fn)] #![stable(feature = "raw_ext", since = "1.1.0")] 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 343cc6e4a8a5a..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; } @@ -232,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; } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 4334d041439d9..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 /// @@ -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 } } diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 3927b2ed9bb5c..c2830d2eb61d1 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -8,10 +8,9 @@ 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 { @@ -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. 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/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 c5d1a893ee809..6f0952c41ede5 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -3,12 +3,10 @@ #![stable(feature = "std_panic", since = "1.9.0")] use crate::any::Any; -use crate::collections; -use crate::fmt; -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( @@ -39,7 +37,7 @@ pub type PanicInfo<'a> = PanicHookInfo<'a>; /// ``` /// /// [`set_hook`]: ../../std/panic/fn.set_hook.html -#[stable(feature = "panic_hook_info", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "panic_hook_info", since = "1.81.0")] #[derive(Debug)] pub struct PanicHookInfo<'a> { payload: &'a (dyn Any + Send), @@ -236,20 +234,17 @@ 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; - -#[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. /// @@ -380,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). @@ -445,13 +440,12 @@ impl BacktraceStyle { } fn from_u8(s: u8) -> Option { - Some(match s { - 0 => return None, - 1 => BacktraceStyle::Short, - 2 => BacktraceStyle::Full, - 3 => BacktraceStyle::Off, - _ => unreachable!(), - }) + match s { + 1 => Some(BacktraceStyle::Short), + 2 => Some(BacktraceStyle::Full), + 3 => Some(BacktraceStyle::Off), + _ => None, + } } } @@ -461,18 +455,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::Relaxed); } - SHOULD_CAPTURE.store(style.as_u8(), Ordering::Release); } /// Checks whether the standard library's panic hook will capture and print a @@ -504,27 +497,24 @@ pub fn get_backtrace_style() -> Option { // to optimize away callers. return None; } - if let Some(style) = BacktraceStyle::from_u8(SHOULD_CAPTURE.load(Ordering::Acquire)) { + + let current = SHOULD_CAPTURE.load(Ordering::Relaxed); + if let Some(style) = BacktraceStyle::from_u8(current) { 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 - }); - set_backtrace_style(format); - Some(format) + 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, + }; + + match SHOULD_CAPTURE.compare_exchange(0, format.as_u8(), Ordering::Relaxed, Ordering::Relaxed) { + Ok(_) => Some(format), + Err(new) => BacktraceStyle::from_u8(new), + } } #[cfg(test)] diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 418a855fb728e..1c972d3810036 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, PanicHookInfo}; 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::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. // @@ -234,6 +231,7 @@ where } /// The default panic handler. +#[optimize(size)] 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. @@ -252,7 +250,8 @@ fn default_hook(info: &PanicHookInfo<'_>) { 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| { + let write = #[optimize(size)] + |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(); @@ -278,7 +277,7 @@ fn default_hook(info: &PanicHookInfo<'_>) { if cfg!(miri) { let _ = writeln!( err, - "note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` \ + "note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` \ for the environment variable to have an effect" ); } @@ -530,6 +529,7 @@ pub unsafe fn r#try R>(f: F) -> Result> // optimizer (in most cases this function is not inlined even as a normal, // non-cold function, though, as of the writing of this comment). #[cold] + #[optimize(size)] unsafe fn cleanup(payload: *mut u8) -> Box { // SAFETY: The whole unsafe block hinges on a correct implementation of // the panic handler `__rust_panic_cleanup`. As such we can only @@ -689,7 +689,7 @@ pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { // lang item for CTFE panic support // never inline unless panic_immediate_abort to avoid code // bloat at the call sites as much as possible -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] #[rustc_do_not_const_check] // hooked by const-eval @@ -759,6 +759,7 @@ fn payload_as_str(payload: &dyn Any) -> &str { /// Executes the primary logic for a panic, including checking for recursive /// panics, panic hooks, and finally dispatching to the panic runtime to either /// abort or unwind. +#[optimize(size)] fn rust_panic_with_hook( payload: &mut dyn PanicPayload, location: &Location<'_>, diff --git a/library/std/src/path.rs b/library/std/src/path.rs index d5121a554bf6c..506ad445b6bed 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -70,23 +70,20 @@ #[cfg(test)] mod tests; +use core::clone::CloneToUninit; + 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 @@ -1797,7 +1794,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] @@ -2079,10 +2076,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, } @@ -2205,7 +2200,7 @@ impl Path { /// Converts a `Path` to a [`Cow`]. /// - /// Any non-Unicode sequences are replaced with + /// Any non-UTF-8 sequences are replaced with /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. /// /// [U+FFFD]: super::char::REPLACEMENT_CHARACTER @@ -3116,6 +3111,16 @@ impl Path { } } +#[unstable(feature = "clone_to_uninit", issue = "126799")] +unsafe impl CloneToUninit for Path { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_to_uninit(&self, dst: *mut Self) { + // SAFETY: Path is just a wrapper around OsStr + unsafe { self.inner.clone_to_uninit(core::ptr::addr_of_mut!((*dst).inner)) } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for Path { #[inline] diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index 3ade4fb892f5e..6436872087d6c 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1,8 +1,10 @@ -use super::*; +use core::hint::black_box; +use super::*; use crate::collections::{BTreeSet, HashSet}; use crate::hash::DefaultHasher; -use core::hint::black_box; +use crate::mem::MaybeUninit; +use crate::ptr; #[allow(unknown_lints, unused_macro_rules)] macro_rules! t ( @@ -2054,3 +2056,20 @@ fn bench_hash_path_long(b: &mut test::Bencher) { black_box(hasher.finish()); } + +#[test] +fn clone_to_uninit() { + let a = Path::new("hello.txt"); + + let mut storage = vec![MaybeUninit::::uninit(); size_of_val::(a)]; + unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()) as *mut Path) }; + assert_eq!(a.as_os_str().as_encoded_bytes(), unsafe { + MaybeUninit::slice_assume_init_ref(&storage) + }); + + let mut b: Box = Path::new("world.exe").into(); + assert_eq!(size_of_val::(a), size_of_val::(&b)); + assert_ne!(a, &*b); + unsafe { a.clone_to_uninit(ptr::from_mut::(&mut b)) }; + assert_eq!(a, &*b); +} 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/process.rs b/library/std/src/process.rs index fc86578a5ff2f..a155855029e70 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -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. /// @@ -620,8 +617,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -702,8 +697,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -751,8 +744,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -789,8 +780,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -825,8 +814,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::{Command, Stdio}; /// use std::env; @@ -873,8 +860,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -903,8 +888,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -931,8 +914,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -962,8 +943,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::{Command, Stdio}; /// @@ -991,8 +970,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::{Command, Stdio}; /// @@ -1020,8 +997,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::{Command, Stdio}; /// @@ -1042,8 +1017,6 @@ impl Command { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -2056,7 +2029,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", @@ -2079,7 +2052,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)) } @@ -2108,8 +2081,6 @@ impl Child { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -2132,8 +2103,6 @@ impl Child { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -2161,8 +2130,6 @@ impl Child { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -2197,8 +2164,6 @@ impl Child { /// /// # Examples /// - /// Basic usage: - /// /// ```no_run /// use std::process::Command; /// @@ -2299,6 +2264,15 @@ impl Child { /// } /// ``` /// +/// In its current implementation, this function will execute exit handlers registered with `atexit` +/// as well as other platform-specific exit handlers (e.g. `fini` sections of ELF shared objects). +/// This means that Rust requires that all exit handlers are safe to execute at any time. In +/// particular, if an exit handler cleans up some state that might be concurrently accessed by other +/// threads, it is required that the exit handler performs suitable synchronization with those +/// threads. (The alternative to this requirement would be to not run exit handlers at all, which is +/// considered undesirable. Note that returning from `main` also calls `exit`, so making `exit` an +/// unsafe operation is not an option.) +/// /// ## Platform-specific behavior /// /// **Unix**: On Unix-like platforms, it is unlikely that all 32 bits of `exit` @@ -2392,15 +2366,11 @@ pub fn abort() -> ! { /// /// # Examples /// -/// Basic usage: -/// /// ```no_run /// use std::process; /// /// println!("My pid is {}", process::id()); /// ``` -/// -/// #[must_use] #[stable(feature = "getpid", since = "1.26.0")] pub fn id() -> u32 { diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 055601d030799..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; diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index deb4a8fa7eed0..b6f36931ec28a 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -16,9 +16,11 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(unused_macros)] +#[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}; @@ -144,7 +146,7 @@ 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. + // Guard against multiple 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 diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs index 08d46f356d9f2..e41cbc1a65c0f 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/condvar.rs @@ -195,8 +195,11 @@ impl Condvar { if poisoned { Err(PoisonError::new(guard)) } else { Ok(guard) } } - /// Blocks the current thread until this condition variable receives a - /// notification and the provided condition is false. + /// Blocks the current thread until the provided condition becomes false. + /// + /// `condition` is checked immediately; if not met (returns `true`), this + /// will [`wait`] for the next notification then check again. This repeats + /// until `condition` returns `false`, in which case this function returns. /// /// This function will atomically unlock the mutex specified (represented by /// `guard`) and block the current thread. This means that any calls @@ -210,6 +213,7 @@ impl Condvar { /// poisoned when this thread re-acquires the lock. For more information, /// see information about [poisoning] on the [`Mutex`] type. /// + /// [`wait`]: Self::wait /// [`notify_one`]: Self::notify_one /// [`notify_all`]: Self::notify_all /// [poisoning]: super::Mutex#poisoning diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 18906aceffa30..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 @@ -175,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: 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 9a38c42f43a02..d0ba8cc3b47df 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -158,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")] @@ -176,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}; -#[stable(feature = "lazy_cell", since = "1.80.0")] -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 185319add745f..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; 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 3478cf41dc9d2..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 { 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 edac7a0cb1835..88a8c75f7c8b9 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; @@ -552,7 +551,7 @@ impl Channel { let mut head = self.head.index.load(Ordering::Acquire); // The channel may be uninitialized, so we have to swap to avoid overwriting any sender's attempts - // to initalize the first block before noticing that the receivers disconnected. Late allocations + // to initialize the first block before noticing that the receivers disconnected. Late allocations // will be deallocated by the sender in Drop. let mut block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel); 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 6d1c9d64e7a7a..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}; diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index feee6948db4fd..26d5b9515a244 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -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. 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 9d969af8c6d84..bf595fdea2d25 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -70,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(); @@ -264,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 94955beaf37b7..be615a5a8ef37 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -167,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 @@ -281,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 /// @@ -373,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 @@ -470,6 +498,7 @@ impl OnceLock { } #[cold] + #[optimize(size)] fn initialize(&self, f: F) -> Result<(), E> where F: FnOnce() -> Result, @@ -488,7 +517,7 @@ impl OnceLock { res = Err(e); // Treat the underlying `Once` as poisoned since we - // failed to initialize our value. Calls + // failed to initialize our value. p.poison(); } } @@ -578,7 +607,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 042c439394e06..0b23681e90726 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 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")] @@ -134,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, } @@ -184,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; } @@ -226,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 }) @@ -308,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 a4ec52a4abe63..d995a16e056d6 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -573,7 +573,7 @@ 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> { @@ -585,7 +585,7 @@ impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { } 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..37a2e41641ac1 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); @@ -20,6 +21,10 @@ fn smoke() { } #[test] +// FIXME: On macOS we use a provenance-incorrect implementation and Miri +// catches that issue with a chance of around 1/1000. +// See for details. +#[cfg_attr(all(miri, target_os = "macos"), ignore)] fn frob() { const N: u32 = 10; const M: usize = if cfg!(miri) { 100 } else { 1000 }; diff --git a/library/std/src/sys/alloc/hermit.rs b/library/std/src/sys/alloc/hermit.rs new file mode 100644 index 0000000000000..77f8200a70a64 --- /dev/null +++ b/library/std/src/sys/alloc/hermit.rs @@ -0,0 +1,27 @@ +use crate::alloc::{GlobalAlloc, Layout, System}; + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let size = layout.size(); + let align = layout.align(); + unsafe { hermit_abi::malloc(size, align) } + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + 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 { + let size = layout.size(); + let align = layout.align(); + unsafe { hermit_abi::realloc(ptr, size, align, new_size) } + } +} diff --git a/library/std/src/sys/alloc/mod.rs b/library/std/src/sys/alloc/mod.rs new file mode 100644 index 0000000000000..2c0b533a5703f --- /dev/null +++ b/library/std/src/sys/alloc/mod.rs @@ -0,0 +1,94 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::ptr; + +// The minimum alignment guaranteed by the architecture. This value is used to +// add fast paths for low alignment values. +#[allow(dead_code)] +const MIN_ALIGN: usize = if cfg!(any( + all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")), + all(target_arch = "xtensa", target_os = "espidf"), +)) { + // The allocator on the esp-idf and zkvm platforms guarantees 4 byte alignment. + 4 +} else if cfg!(any( + target_arch = "x86", + target_arch = "arm", + target_arch = "m68k", + target_arch = "csky", + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "sparc", + target_arch = "wasm32", + target_arch = "hexagon", + target_arch = "riscv32", + target_arch = "xtensa", +)) { + 8 +} else if cfg!(any( + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "arm64ec", + target_arch = "loongarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "s390x", + target_arch = "sparc64", + target_arch = "riscv64", + target_arch = "wasm64", +)) { + 16 +} else { + panic!("add a value for MIN_ALIGN") +}; + +#[allow(dead_code)] +unsafe fn realloc_fallback( + alloc: &System, + ptr: *mut u8, + old_layout: Layout, + new_size: usize, +) -> *mut u8 { + // 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 = usize::min(old_layout.size(), new_size); + ptr::copy_nonoverlapping(ptr, new_ptr, size); + GlobalAlloc::dealloc(alloc, ptr, old_layout); + } + + new_ptr + } +} + +cfg_if::cfg_if! { + if #[cfg(any( + target_family = "unix", + target_os = "wasi", + target_os = "teeos", + ))] { + mod unix; + } else if #[cfg(target_os = "windows")] { + mod windows; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod sgx; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + } else if #[cfg(target_family = "wasm")] { + mod wasm; + } else if #[cfg(target_os = "xous")] { + mod xous; + } else if #[cfg(target_os = "zkvm")] { + mod zkvm; + } +} diff --git a/library/std/src/sys/pal/sgx/alloc.rs b/library/std/src/sys/alloc/sgx.rs similarity index 95% rename from library/std/src/sys/pal/sgx/alloc.rs rename to library/std/src/sys/alloc/sgx.rs index 0c7bf9a9201f2..fca9d087e5bfc 100644 --- a/library/std/src/sys/pal/sgx/alloc.rs +++ b/library/std/src/sys/alloc/sgx.rs @@ -1,9 +1,8 @@ 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::sync::atomic::{AtomicBool, Ordering}; +use crate::sys::pal::abi::mem as sgx_mem; +use crate::sys::pal::waitqueue::SpinMutex; // Using a SpinMutex because we never want to exit the enclave waiting for the // allocator. diff --git a/library/std/src/sys/pal/solid/alloc.rs b/library/std/src/sys/alloc/solid.rs similarity index 89% rename from library/std/src/sys/pal/solid/alloc.rs rename to library/std/src/sys/alloc/solid.rs index d013bd8761003..abb534a1c5cf4 100644 --- a/library/std/src/sys/pal/solid/alloc.rs +++ b/library/std/src/sys/alloc/solid.rs @@ -1,7 +1,5 @@ -use crate::{ - alloc::{GlobalAlloc, Layout, System}, - sys::common::alloc::{realloc_fallback, MIN_ALIGN}, -}; +use super::{realloc_fallback, MIN_ALIGN}; +use crate::alloc::{GlobalAlloc, Layout, System}; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { diff --git a/library/std/src/sys/pal/uefi/alloc.rs b/library/std/src/sys/alloc/uefi.rs similarity index 98% rename from library/std/src/sys/pal/uefi/alloc.rs rename to library/std/src/sys/alloc/uefi.rs index 15404ac3ea696..5221876e90866 100644 --- a/library/std/src/sys/pal/uefi/alloc.rs +++ b/library/std/src/sys/alloc/uefi.rs @@ -3,9 +3,9 @@ use r_efi::protocols::loaded_image; -use super::helpers; use crate::alloc::{GlobalAlloc, Layout, System}; use crate::sync::OnceLock; +use crate::sys::pal::helpers; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { diff --git a/library/std/src/sys/pal/unix/alloc.rs b/library/std/src/sys/alloc/unix.rs similarity index 81% rename from library/std/src/sys/pal/unix/alloc.rs rename to library/std/src/sys/alloc/unix.rs index eb3a57c212b4a..46ed7de7162f8 100644 --- a/library/std/src/sys/pal/unix/alloc.rs +++ b/library/std/src/sys/alloc/unix.rs @@ -1,6 +1,6 @@ +use super::{realloc_fallback, MIN_ALIGN}; use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ptr; -use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { @@ -11,7 +11,7 @@ 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 { // `posix_memalign` returns a non-aligned value if supplied a very // large alignment on older versions of Apple's platforms (unknown @@ -25,7 +25,7 @@ unsafe impl GlobalAlloc for System { return ptr::null_mut(); } } - aligned_malloc(&layout) + unsafe { aligned_malloc(&layout) } } } @@ -33,11 +33,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 } @@ -45,15 +45,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) } } } } @@ -67,7 +67,7 @@ cfg_if::cfg_if! { ))] { #[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] @@ -81,7 +81,7 @@ cfg_if::cfg_if! { // 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::()); - 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/wasm/alloc.rs b/library/std/src/sys/alloc/wasm.rs similarity index 98% rename from library/std/src/sys/pal/wasm/alloc.rs rename to library/std/src/sys/alloc/wasm.rs index b74ce0d47425a..ef9d753d7f86c 100644 --- a/library/std/src/sys/pal/wasm/alloc.rs +++ b/library/std/src/sys/alloc/wasm.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/windows/alloc.rs b/library/std/src/sys/alloc/windows.rs similarity index 95% rename from library/std/src/sys/pal/windows/alloc.rs rename to library/std/src/sys/alloc/windows.rs index 987be6b69eec9..e91956966aa73 100644 --- a/library/std/src/sys/pal/windows/alloc.rs +++ b/library/std/src/sys/alloc/windows.rs @@ -1,10 +1,10 @@ +use super::{realloc_fallback, MIN_ALIGN}; use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ffi::c_void; +use crate::mem::MaybeUninit; use crate::ptr; use crate::sync::atomic::{AtomicPtr, Ordering}; -use crate::sys::c::{self, windows_targets}; -use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; -use core::mem::MaybeUninit; +use crate::sys::c; #[cfg(test)] mod tests; @@ -37,7 +37,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> c::HANDLE) // 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 core::ffi::c_void); +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, @@ -61,9 +61,9 @@ windows_targets::link!("kernel32.dll" "system" fn HeapAlloc(hheap: c::HANDLE, dw windows_targets::link!("kernel32.dll" "system" fn HeapReAlloc( hheap: c::HANDLE, dwflags : u32, - lpmem: *const core::ffi::c_void, + lpmem: *const c_void, dwbytes: usize -) -> *mut core::ffi::c_void); +) -> *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. @@ -79,7 +79,7 @@ windows_targets::link!("kernel32.dll" "system" fn HeapReAlloc( // 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 core::ffi::c_void) -> c::BOOL); +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. @@ -112,28 +112,28 @@ fn init_or_get_process_heap() -> c::HANDLE { extern "C" fn process_heap_init_and_alloc( _heap: MaybeUninit, // We pass this argument to match the ABI of `HeapAlloc` flags: u32, - dwBytes: usize, + bytes: usize, ) -> *mut c_void { let heap = init_or_get_process_heap(); if core::intrinsics::unlikely(heap.is_null()) { return ptr::null_mut(); } // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`. - unsafe { HeapAlloc(heap, flags, dwBytes) } + unsafe { HeapAlloc(heap, flags, bytes) } } #[inline(never)] fn process_heap_alloc( _heap: MaybeUninit, // We pass this argument to match the ABI of `HeapAlloc`, flags: u32, - dwBytes: usize, + bytes: 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`. - unsafe { HeapAlloc(heap, flags, dwBytes) } + unsafe { HeapAlloc(heap, flags, bytes) } } else { - process_heap_init_and_alloc(MaybeUninit::uninit(), flags, dwBytes) + process_heap_init_and_alloc(MaybeUninit::uninit(), flags, bytes) } } diff --git a/library/std/src/sys/pal/windows/alloc/tests.rs b/library/std/src/sys/alloc/windows/tests.rs similarity index 100% rename from library/std/src/sys/pal/windows/alloc/tests.rs rename to library/std/src/sys/alloc/windows/tests.rs diff --git a/library/std/src/sys/pal/xous/alloc.rs b/library/std/src/sys/alloc/xous.rs similarity index 96% rename from library/std/src/sys/pal/xous/alloc.rs rename to library/std/src/sys/alloc/xous.rs index 601411173aacb..9ea43445d0206 100644 --- a/library/std/src/sys/pal/xous/alloc.rs +++ b/library/std/src/sys/alloc/xous.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/zkvm/alloc.rs b/library/std/src/sys/alloc/zkvm.rs similarity index 94% rename from library/std/src/sys/pal/zkvm/alloc.rs rename to library/std/src/sys/alloc/zkvm.rs index 2fdca22352470..a600cfa2220dd 100644 --- a/library/std/src/sys/pal/zkvm/alloc.rs +++ b/library/std/src/sys/alloc/zkvm.rs @@ -1,5 +1,5 @@ -use super::abi; use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::sys::pal::abi; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { 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/backtrace.rs b/library/std/src/sys/backtrace.rs index 7401d8ce32087..4d939e175cf2e 100644 --- a/library/std/src/sys/backtrace.rs +++ b/library/std/src/sys/backtrace.rs @@ -1,13 +1,12 @@ //! Common code for printing backtraces. +#![forbid(unsafe_op_in_unsafe_fn)] use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt}; use crate::borrow::Cow; -use crate::env; -use crate::fmt; -use crate::io; use crate::io::prelude::*; use crate::path::{self, Path, PathBuf}; use crate::sync::{Mutex, MutexGuard, PoisonError}; +use crate::{env, fmt, io}; /// Max number of frames to print. const MAX_NB_FRAMES: usize = 100; @@ -62,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); } - 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, + ); + } + 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/cmath.rs b/library/std/src/sys/cmath.rs index 99df503b82de2..2997e908fa1b2 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -28,6 +28,21 @@ extern "C" { pub fn lgamma_r(n: f64, s: &mut i32) -> f64; pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; + pub fn acosf128(n: f128) -> f128; + pub fn asinf128(n: f128) -> f128; + pub fn atanf128(n: f128) -> f128; + pub fn atan2f128(a: f128, b: f128) -> f128; + pub fn cbrtf128(n: f128) -> f128; + pub fn coshf128(n: f128) -> f128; + pub fn expm1f128(n: f128) -> f128; + pub fn hypotf128(x: f128, y: f128) -> f128; + pub fn log1pf128(n: f128) -> f128; + pub fn sinhf128(n: f128) -> f128; + pub fn tanf128(n: f128) -> f128; + pub fn tanhf128(n: f128) -> f128; + pub fn tgammaf128(n: f128) -> f128; + pub fn lgammaf128_r(n: f128, s: &mut i32) -> f128; + cfg_if::cfg_if! { if #[cfg(not(all(target_os = "windows", target_env = "msvc", target_arch = "x86")))] { pub fn acosf(n: f32) -> f32; diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index e50758ce00d8b..1ef17dd530fd2 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -5,8 +5,10 @@ /// descriptors. mod pal; +mod alloc; mod personality; +pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; pub mod exit_guard; diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index 2a7477e3afc20..992767211d083 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -1,15 +1,16 @@ //! The underlying OsString/OsStr implementation on Unix and many other //! systems: just a `Vec`/`[u8]`. +use core::clone::CloneToUninit; +use core::ptr::addr_of_mut; + 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; @@ -347,3 +348,13 @@ impl Slice { self.inner.eq_ignore_ascii_case(&other.inner) } } + +#[unstable(feature = "clone_to_uninit", issue = "126799")] +unsafe impl CloneToUninit for Slice { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_to_uninit(&self, dst: *mut Self) { + // SAFETY: we're just a wrapper around [u8] + unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) } + } +} diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index 806bf033dbc94..433237aa6e7bf 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -1,13 +1,15 @@ //! The underlying OsString/OsStr implementation on Windows is a //! wrapper around the "WTF-8" encoding; see the `wtf8` module for more. +use core::clone::CloneToUninit; +use core::ptr::addr_of_mut; + 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 { @@ -269,3 +271,13 @@ impl Slice { self.inner.eq_ignore_ascii_case(&other.inner) } } + +#[unstable(feature = "clone_to_uninit", issue = "126799")] +unsafe impl CloneToUninit for Slice { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_to_uninit(&self, dst: *mut Self) { + // SAFETY: we're just a wrapper around Wtf8 + unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) } + } +} diff --git a/library/std/src/sys/pal/common/alloc.rs b/library/std/src/sys/pal/common/alloc.rs deleted file mode 100644 index 598b6db71f5de..0000000000000 --- a/library/std/src/sys/pal/common/alloc.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::cmp; -use crate::ptr; - -// The minimum alignment guaranteed by the architecture. This value is used to -// add fast paths for low alignment values. -#[cfg(any( - target_arch = "x86", - target_arch = "arm", - target_arch = "m68k", - target_arch = "csky", - target_arch = "mips", - target_arch = "mips32r6", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "sparc", - target_arch = "wasm32", - target_arch = "hexagon", - all(target_arch = "riscv32", not(any(target_os = "espidf", target_os = "zkvm"))), - all(target_arch = "xtensa", not(target_os = "espidf")), -))] -pub const MIN_ALIGN: usize = 8; -#[cfg(any( - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "arm64ec", - target_arch = "loongarch64", - target_arch = "mips64", - target_arch = "mips64r6", - target_arch = "s390x", - target_arch = "sparc64", - target_arch = "riscv64", - target_arch = "wasm64", -))] -pub const MIN_ALIGN: usize = 16; -// The allocator on the esp-idf and zkvm platforms guarantee 4 byte alignment. -#[cfg(all(any( - all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")), - all(target_arch = "xtensa", target_os = "espidf"), -)))] -pub const MIN_ALIGN: usize = 4; - -pub unsafe fn realloc_fallback( - alloc: &System, - ptr: *mut u8, - 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()); - - 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 -} diff --git a/library/std/src/sys/pal/common/mod.rs b/library/std/src/sys/pal/common/mod.rs index 29fc0835d7666..9af4dee401cf3 100644 --- a/library/std/src/sys/pal/common/mod.rs +++ b/library/std/src/sys/pal/common/mod.rs @@ -10,7 +10,6 @@ #![allow(dead_code)] -pub mod alloc; pub mod small_c_string; #[cfg(test)] 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 deleted file mode 100644 index 2cd0db909403b..0000000000000 --- a/library/std/src/sys/pal/hermit/alloc.rs +++ /dev/null @@ -1,31 +0,0 @@ -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 - } - - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - hermit_abi::free(ptr, layout.size(), layout.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) - } -} 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 3c52b85de23a2..79fc13bd4a87f 100644 --- a/library/std/src/sys/pal/hermit/fd.rs +++ b/library/std/src/sys/pal/hermit/fd.rs @@ -3,13 +3,10 @@ use super::hermit_abi; use crate::cmp; use crate::io::{self, IoSlice, IoSliceMut, Read}; -use crate::os::hermit::io::{FromRawFd, OwnedFd, RawFd}; -use crate::sys::cvt; -use crate::sys::unsupported; +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 } @@ -114,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 e4e9eee044efa..aaf1a044d0613 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -4,21 +4,17 @@ use super::hermit_abi::{ 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_common::{AsInner, AsInnerMut, FromInner, IntoInner}; - +use crate::sys::{cvt, unsupported}; pub use crate::sys_common::fs::{copy, exists}; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::{fmt, mem}; #[derive(Debug)] pub struct File(FileDesc); @@ -488,7 +484,8 @@ 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) } } diff --git a/library/std/src/sys/pal/hermit/io.rs b/library/std/src/sys/pal/hermit/io.rs index 9de7b53e53c03..aad1eef71e9b0 100644 --- a/library/std/src/sys/pal/hermit/io.rs +++ b/library/std/src/sys/pal/hermit/io.rs @@ -1,9 +1,9 @@ +use hermit_abi::{c_void, iovec}; + use crate::marker::PhantomData; use crate::os::hermit::io::{AsFd, AsRawFd}; use crate::slice; -use hermit_abi::{c_void, iovec}; - #[derive(Copy, Clone)] #[repr(transparent)] pub struct IoSlice<'a> { diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 55583b89d6714..1f2e5d9469f5c 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -13,11 +13,11 @@ //! 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; -pub mod alloc; pub mod args; pub mod env; pub mod fd; @@ -49,9 +49,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) { @@ -80,7 +78,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. @@ -101,10 +101,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) }; - crate::sys::thread_local::destructors::run(); - 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 6016d50eba085..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; diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index a7a73c756f216..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,30 +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 unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let (k, v) = (k.to_owned(), v.to_owned()); - ENV.as_ref().unwrap().lock().unwrap().insert(k, v); + ENV.lock().unwrap().as_mut().unwrap().insert(k, v); Ok(()) } pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { - ENV.as_ref().unwrap().lock().unwrap().remove(k); + ENV.lock().unwrap().as_mut().unwrap().remove(k); Ok(()) } @@ -192,9 +186,7 @@ pub fn home_dir() -> Option { } pub fn exit(code: i32) -> ! { - unsafe { - hermit_abi::exit(code); - } + unsafe { hermit_abi::exit(code) } } pub fn getpid() -> u32 { diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index a244b953d2a49..4c0c0919f4799 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -2,11 +2,10 @@ use super::hermit_abi; 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; @@ -26,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 }) @@ -55,7 +58,9 @@ impl Thread { } 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] @@ -72,8 +77,11 @@ impl Thread { #[inline] pub fn sleep(dur: Duration) { + let micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; + let micros = u64::try_from(micros).unwrap_or(u64::MAX); + unsafe { - hermit_abi::usleep(dur.as_micros() as u64); + hermit_abi::usleep(micros); } } @@ -90,9 +98,7 @@ impl Thread { #[inline] pub fn into_id(self) -> Tid { - let id = self.tid; - mem::forget(self); - id + ManuallyDrop::new(self).tid } } diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index e0cb7c2aa98a5..2c87c4860a27a 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -1,10 +1,11 @@ #![allow(dead_code)] +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; 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 fd7b5558f7566..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}, - 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, @@ -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 df0176244489a..9be018c8a5312 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -76,23 +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) } - } - } -} - #[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..5069ab82ccc90 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,17 @@ #![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::pin::PinCoerceUnsized; 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 +66,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 +76,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 +175,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 +266,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 +275,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 +286,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::()) } @@ -754,6 +752,9 @@ where #[unstable(feature = "sgx_platform", issue = "56975")] impl, U> CoerceUnsized> for UserRef {} +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for UserRef {} + #[unstable(feature = "sgx_platform", issue = "56975")] impl Index for UserRef<[T]> where 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/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 851ab9b9f9767..8d29b2ec6193e 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -9,7 +9,6 @@ use crate::io::ErrorKind; use crate::sync::atomic::{AtomicBool, Ordering}; pub mod abi; -pub mod alloc; pub mod args; pub mod env; pub mod fd; 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 c021300d4ae33..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 diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index 446cdd18b7e42..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; 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 75efaaac2a948..394be15b0064b 100644 --- a/library/std/src/sys/pal/solid/abi/fs.rs +++ b/library/std/src/sys/pal/solid/abi/fs.rs @@ -1,11 +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/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 dc83e4f4b4999..bce9aa6d99cd1 100644 --- a/library/std/src/sys/pal/solid/fs.rs +++ b/library/std/src/sys/pal/solid/fs.rs @@ -1,18 +1,16 @@ 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, -}; - +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; +use crate::sys_common::ignore_notfound; /// A file descriptor. #[derive(Clone, Copy)] @@ -530,15 +528,23 @@ pub fn rmdir(p: &Path) -> io::Result<()> { pub fn remove_dir_all(path: &Path) -> io::Result<()> { for child in readdir(path)? { - let child = child?; - let child_type = child.file_type()?; - if child_type.is_dir() { - remove_dir_all(&child.path())?; - } else { - unlink(&child.path())?; + let result: io::Result<()> = try { + let child = child?; + let child_type = child.file_type()?; + if child_type.is_dir() { + remove_dir_all(&child.path())?; + } else { + unlink(&child.path())?; + } + }; + // ignore internal NotFound errors + if let Err(err) = &result + && err.kind() != io::ErrorKind::NotFound + { + return result; } } - rmdir(path) + ignore_notfound(rmdir(path)) } pub fn readlink(p: &Path) -> io::Result { 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 9a7741ddda71e..6ebcf5b7c48c8 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; @@ -16,7 +16,6 @@ pub mod itron { use super::unsupported; } -pub mod alloc; #[path = "../unsupported/args.rs"] pub mod args; pub mod env; @@ -32,8 +31,7 @@ pub mod pipe; #[path = "../unsupported/process.rs"] pub mod process; pub mod stdio; -pub use self::itron::thread; -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 ac90aae4ebe46..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 { 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 deleted file mode 100644 index b280d1dd76f7a..0000000000000 --- a/library/std/src/sys/pal/teeos/alloc.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::ptr; -use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; - -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // jemalloc provides alignment less than MIN_ALIGN for small allocations. - // So only rely on MIN_ALIGN if size >= align. - // Also see and - // . - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - unsafe { libc::malloc(layout.size()) as *mut u8 } - } else { - unsafe { aligned_malloc(&layout) } - } - } - - #[inline] - 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() { - unsafe { libc::calloc(layout.size(), 1) as *mut u8 } - } else { - let ptr = unsafe { self.alloc(layout) }; - if !ptr.is_null() { - unsafe { ptr::write_bytes(ptr, 0, layout.size()) }; - } - ptr - } - } - - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - 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 { - unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 } - } else { - unsafe { realloc_fallback(self, ptr, layout, new_size) } - } - } -} - -#[inline] -unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { - let mut out = ptr::null_mut(); - // 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 = 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 adefd1bb42c8d..00e3860424006 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -8,7 +8,6 @@ pub use self::rand::hashmap_random_keys; -pub mod alloc; #[path = "../unsupported/args.rs"] pub mod args; #[path = "../unsupported/env.rs"] diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index 3be0846a6dd4d..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 } 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 7a27d749f1c9c..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; @@ -115,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 { @@ -127,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/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 4d50d9e8c3d9c..ac22f4ded8855 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -13,11 +13,11 @@ //! [`OsString`]: crate::ffi::OsString #![forbid(unsafe_op_in_unsafe_fn)] -pub mod alloc; pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; +pub mod helpers; #[path = "../unsupported/io.rs"] pub mod io; #[path = "../unsupported/net.rs"] @@ -25,14 +25,11 @@ 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; pub mod time; -mod helpers; - #[cfg(test)] mod tests; @@ -102,9 +99,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 0b27977df2fde..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 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..0cc9cecb89db0 --- /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 calculation + 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/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/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 e2ec838b740cb..a943e3a581a83 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) { @@ -113,6 +112,7 @@ impl DoubleEndedIterator for Args { target_os = "aix", target_os = "nto", target_os = "hurd", + target_os = "rtems", ))] mod imp { use crate::ffi::c_char; diff --git a/library/std/src/sys/pal/unix/env.rs b/library/std/src/sys/pal/unix/env.rs index fb1f868644d48..b2d399b8791b5 100644 --- a/library/std/src/sys/pal/unix/env.rs +++ b/library/std/src/sys/pal/unix/env.rs @@ -240,6 +240,17 @@ pub mod os { pub const EXE_EXTENSION: &str = ""; } +#[cfg(target_os = "rtems")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "rtems"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + #[cfg(target_os = "vxworks")] pub mod os { pub const FAMILY: &str = "unix"; 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 8308a48f16a9e..4ec577a0a01d0 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -4,29 +4,6 @@ #[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(all(target_os = "linux", target_env = "gnu"))] -use crate::sys::weak::syscall; -#[cfg(target_os = "android")] -use crate::sys::weak::weak; - -use libc::{c_int, mode_t}; - #[cfg(all(target_os = "linux", target_env = "gnu"))] use libc::c_char; #[cfg(any( @@ -73,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, @@ -97,7 +75,24 @@ use libc::{ ))] use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64}; +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); @@ -463,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) } } @@ -483,6 +478,7 @@ impl FileAttr { target_os = "horizon", target_os = "vita", target_os = "hurd", + target_os = "rtems", )))] pub fn modified(&self) -> io::Result { #[cfg(target_pointer_width = "32")] @@ -495,7 +491,12 @@ impl FileAttr { SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64) } - #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))] + #[cfg(any( + target_os = "vxworks", + target_os = "espidf", + target_os = "vita", + target_os = "rtems", + ))] pub fn modified(&self) -> io::Result { SystemTime::new(self.stat.st_mtime as i64, 0) } @@ -511,6 +512,7 @@ impl FileAttr { target_os = "horizon", target_os = "vita", target_os = "hurd", + target_os = "rtems", )))] pub fn accessed(&self) -> io::Result { #[cfg(target_pointer_width = "32")] @@ -523,7 +525,12 @@ impl FileAttr { SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64) } - #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))] + #[cfg(any( + target_os = "vxworks", + target_os = "espidf", + target_os = "vita", + target_os = "rtems" + ))] pub fn accessed(&self) -> io::Result { SystemTime::new(self.stat.st_atime as i64, 0) } @@ -857,6 +864,8 @@ impl Drop for Dir { target_os = "espidf", target_os = "fuchsia", target_os = "horizon", + target_os = "vxworks", + target_os = "rtems", )))] { let fd = unsafe { libc::dirfd(self.0) }; @@ -974,6 +983,7 @@ impl DirEntry { target_os = "aix", target_os = "nto", target_os = "hurd", + target_os = "rtems", target_vendor = "apple", ))] pub fn ino(&self) -> u64 { @@ -1313,7 +1323,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!( @@ -1327,10 +1342,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, @@ -1550,17 +1566,6 @@ impl fmt::Debug for File { None } - #[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - 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)> { let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; if mode == -1 { @@ -1574,22 +1579,6 @@ impl fmt::Debug for File { } } - #[cfg(not(any( - target_os = "linux", - target_os = "freebsd", - target_os = "hurd", - 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)> { - // FIXME(#24570): implement this for other Unix platforms - None - } - let fd = self.as_raw_fd(); let mut b = f.debug_struct("File"); b.field("fd", &fd); @@ -1742,7 +1731,7 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { run_path_with_cstr(original, &|original| { run_path_with_cstr(link, &|link| { cfg_if::cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { + if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nto"))] { // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves // it implementation-defined whether `link` follows symlinks, so rely on the // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. @@ -1962,6 +1951,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) }) @@ -1969,11 +1959,23 @@ 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, Vxworks and Miri @@ -2001,6 +2003,11 @@ mod remove_dir_impl { 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; @@ -2009,11 +2016,7 @@ mod remove_dir_impl { use crate::path::{Path, PathBuf}; use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::{cvt, cvt_r}; - - #[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 crate::sys_common::ignore_notfound; pub fn openat_nofollow_dironly(parent_fd: Option, p: &CStr) -> io::Result { let fd = cvt_r(|| unsafe { @@ -2067,6 +2070,16 @@ mod remove_dir_impl { } } + fn is_enoent(result: &io::Result<()>) -> bool { + if let Err(err) = result + && matches!(err.raw_os_error(), Some(libc::ENOENT)) + { + true + } else { + false + } + } + fn remove_dir_all_recursive(parent_fd: Option, path: &CStr) -> io::Result<()> { // try opening as directory let fd = match openat_nofollow_dironly(parent_fd, &path) { @@ -2090,27 +2103,35 @@ mod remove_dir_impl { for child in dir { let child = child?; let child_name = child.name_cstr(); - match is_dir(&child) { - Some(true) => { - remove_dir_all_recursive(Some(fd), child_name)?; - } - Some(false) => { - cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) })?; - } - None => { - // POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed - // if the process has the appropriate privileges. This however can causing orphaned - // directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing - // into it first instead of trying to unlink() it. - remove_dir_all_recursive(Some(fd), child_name)?; + // we need an inner try block, because if one of these + // directories has already been deleted, then we need to + // continue the loop, not return ok. + let result: io::Result<()> = try { + match is_dir(&child) { + Some(true) => { + remove_dir_all_recursive(Some(fd), child_name)?; + } + Some(false) => { + cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) })?; + } + None => { + // POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed + // if the process has the appropriate privileges. This however can causing orphaned + // directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing + // into it first instead of trying to unlink() it. + remove_dir_all_recursive(Some(fd), child_name)?; + } } + }; + if result.is_err() && !is_enoent(&result) { + return result; } } // unlink the directory after removing its contents - cvt(unsafe { + ignore_notfound(cvt(unsafe { unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), path.as_ptr(), libc::AT_REMOVEDIR) - })?; + }))?; Ok(()) } diff --git a/library/std/src/sys/pal/unix/futex.rs b/library/std/src/sys/pal/unix/futex.rs index b8900da4cddb5..cc725045c4810 100644 --- a/library/std/src/sys/pal/unix/futex.rs +++ b/library/std/src/sys/pal/unix/futex.rs @@ -16,7 +16,7 @@ pub type SmallAtomic = AtomicU32; /// Must be the underlying type of SmallAtomic pub type SmallPrimitive = u32; -/// Wait for a futex_wake operation to wake us. +/// Waits for a `futex_wake` operation to wake us. /// /// Returns directly if the futex doesn't hold the expected value. /// @@ -87,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. @@ -100,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 cd38b7c07e2b1..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())) 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/l4re.rs b/library/std/src/sys/pal/unix/l4re.rs index fe9559f2a569f..52d39dcfb16fb 100644 --- a/library/std/src/sys/pal/unix/l4re.rs +++ b/library/std/src/sys/pal/unix/l4re.rs @@ -54,6 +54,10 @@ pub mod net { unimpl!(); } + pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { + unimpl!(); + } + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { unimpl!(); } diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 262f9c704a882..e8428eccb1691 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -1,15 +1,12 @@ #![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; @@ -82,15 +79,17 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "l4re", target_os = "horizon", target_os = "vita", + target_os = "rtems", // The poll on Darwin doesn't set POLLNVAL for closed fds. 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 }, @@ -117,7 +116,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { if pfd.revents & libc::POLLNVAL == 0 { continue; } - if open64(c"/dev/null".as_ptr().cast(), libc::O_RDWR, 0) == -1 { + if open64(c"/dev/null".as_ptr(), libc::O_RDWR, 0) == -1 { // If the stream is closed but we failed to reopen it, abort the // process. Otherwise we wouldn't preserve the safety of // operations on the corresponding Rust object Stdin, Stdout, or @@ -140,14 +139,15 @@ 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 { + if open64(c"/dev/null".as_ptr(), libc::O_RDWR, 0) == -1 { // If the stream is closed but we failed to reopen it, abort the // process. Otherwise we wouldn't preserve the safety of // operations on the corresponding Rust object Stdin, Stdout, or @@ -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,7 +308,7 @@ macro_rules! impl_is_minus_one { impl_is_minus_one! { i8 i16 i32 i64 isize } -/// Convert native return values to Result using the *-1 means error is in `errno`* convention. +/// 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) } diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index b8dc1538a6378..d75a666d350ff 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 + // connection 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 9adc2b94e599e..503f8915256ee 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; @@ -40,7 +31,7 @@ cfg_if::cfg_if! { } extern "C" { - #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))] + #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] #[cfg_attr( any( target_os = "linux", @@ -70,13 +61,14 @@ extern "C" { } /// Returns the platform-specific value of errno -#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))] +#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] pub fn errno() -> i32 { unsafe { (*errno_location()) as i32 } } /// Sets the platform-specific value of errno -#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks")))] // needed for readdir and syscall! +// needed for readdir and syscall! +#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks"), not(target_os = "rtems")))] #[allow(dead_code)] // but not all target cfgs actually end up using it pub fn set_errno(e: i32) { unsafe { *errno_location() = e as c_int } @@ -87,6 +79,16 @@ pub fn errno() -> i32 { unsafe { libc::errnoGet() } } +#[cfg(target_os = "rtems")] +pub fn errno() -> i32 { + extern "C" { + #[thread_local] + static _tls_errno: c_int; + } + + unsafe { _tls_errno as i32 } +} + #[cfg(target_os = "dragonfly")] pub fn errno() -> i32 { extern "C" { @@ -248,13 +250,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, @@ -482,7 +483,7 @@ pub fn current_exe() -> io::Result { } } -#[cfg(target_os = "redox")] +#[cfg(any(target_os = "redox", target_os = "rtems"))] pub fn current_exe() -> io::Result { crate::fs::read_to_string("sys:exe").map(PathBuf::from) } @@ -513,13 +514,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, 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 abd4a334783e4..4bb22f3670978 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -1,20 +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::sys::pal::unix::linux::pidfd::PidFd; - #[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", @@ -23,8 +10,17 @@ 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"))] { + // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0 + if #[cfg(any(target_env = "nto70", target_env = "nto71"))] { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; use crate::time::Duration; @@ -194,7 +190,8 @@ impl Command { #[cfg(not(any( target_os = "watchos", target_os = "tvos", - all(target_os = "nto", target_env = "nto71"), + target_env = "nto70", + target_env = "nto71" )))] unsafe fn do_fork(&mut self) -> Result { cvt(libc::fork()) @@ -204,7 +201,8 @@ impl Command { // or closed a file descriptor while the fork() was occurring". // Documentation says "... or try calling fork() again". This is what we do here. // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html - #[cfg(all(target_os = "nto", target_env = "nto71"))] + // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0 + #[cfg(any(target_env = "nto70", target_env = "nto71"))] unsafe fn do_fork(&mut self) -> Result { use crate::sys::os::errno; @@ -446,11 +444,12 @@ 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}; - #[cfg(target_os = "linux")] - use core::sync::atomic::{AtomicU8, Ordering}; if self.get_gid().is_some() || self.get_uid().is_some() @@ -541,7 +540,7 @@ impl Command { // or closed a file descriptor while the posix_spawn() was occurring". // Documentation says "... or try calling posix_spawn() again". This is what we do here. // See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html - #[cfg(all(target_os = "nto", target_env = "nto71"))] + #[cfg(target_os = "nto")] unsafe fn retrying_libc_posix_spawnp( pid: *mut pid_t, file: *const c_char, @@ -762,10 +761,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(); @@ -819,11 +819,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]>(); @@ -1047,7 +1047,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 @@ -1089,13 +1089,13 @@ fn signal_string(signal: i32) -> &'static str { libc::SIGURG => " (SIGURG)", #[cfg(not(target_os = "l4re"))] libc::SIGXCPU => " (SIGXCPU)", - #[cfg(not(target_os = "l4re"))] + #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] libc::SIGXFSZ => " (SIGXFSZ)", - #[cfg(not(target_os = "l4re"))] + #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] libc::SIGVTALRM => " (SIGVTALRM)", #[cfg(not(target_os = "l4re"))] libc::SIGPROF => " (SIGPROF)", - #[cfg(not(target_os = "l4re"))] + #[cfg(not(any(target_os = "l4re", target_os = "rtems")))] libc::SIGWINCH => " (SIGWINCH)", #[cfg(not(any(target_os = "haiku", target_os = "l4re")))] libc::SIGIO => " (SIGIO)", @@ -1189,12 +1189,11 @@ impl ExitStatusError { #[cfg(target_os = "linux")] mod linux_child_ext { - use crate::io; - use crate::mem; 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 { 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 e5e1f956bc351..f4d6ac6b4e340 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 @@ -24,7 +24,20 @@ fn exitstatus_display_tests() { // The purpose of this test is to test our string formatting, not our understanding of the wait // status magic numbers. So restrict these to Linux. if cfg!(target_os = "linux") { + #[cfg(any(target_arch = "mips", target_arch = "mips64"))] + t(0x0137f, "stopped (not terminated) by signal: 19 (SIGPWR)"); + + #[cfg(any(target_arch = "sparc", target_arch = "sparc64"))] + t(0x0137f, "stopped (not terminated) by signal: 19 (SIGCONT)"); + + #[cfg(not(any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "sparc", + target_arch = "sparc64" + )))] t(0x0137f, "stopped (not terminated) by signal: 19 (SIGSTOP)"); + t(0x0ffff, "continued (WIFCONTINUED)"); } 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 973188b1f2b27..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 @@ -2,12 +2,11 @@ //! //! 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 5007dbd34b4ab..0477b3d9a70da 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,12 @@ -use crate::fmt; +#![forbid(unsafe_op_in_unsafe_fn)] +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 +68,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..cc0852aab4396 100644 --- a/library/std/src/sys/pal/unix/rand.rs +++ b/library/std/src/sys/pal/unix/rand.rs @@ -2,7 +2,9 @@ pub fn hashmap_random_keys() -> (u64, u64) { const KEY_LEN: usize = core::mem::size_of::(); let mut v = [0u8; KEY_LEN * 2]; - imp::fill_bytes(&mut v); + if let Err(err) = read(&mut v) { + panic!("failed to retrieve random hash map seed: {err}"); + } let key1 = v[0..KEY_LEN].try_into().unwrap(); let key2 = v[KEY_LEN..].try_into().unwrap(); @@ -10,28 +12,78 @@ pub fn hashmap_random_keys() -> (u64, u64) { (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2)) } -#[cfg(all( - unix, - not(target_os = "openbsd"), - not(target_os = "netbsd"), - not(target_os = "fuchsia"), - not(target_os = "redox"), - not(target_os = "vxworks"), - not(target_os = "emscripten"), - not(target_os = "vita"), - not(target_vendor = "apple"), +cfg_if::cfg_if! { + if #[cfg(any( + target_vendor = "apple", + target_os = "openbsd", + target_os = "emscripten", + target_os = "vita", + all(target_os = "netbsd", not(netbsd10)), + target_os = "fuchsia", + target_os = "vxworks", + ))] { + // Some systems have a syscall that directly retrieves random data. + // If that is guaranteed to be available, use it. + use imp::syscall as read; + } else { + // Otherwise, try the syscall to see if it exists only on some systems + // and fall back to reading from the random device otherwise. + fn read(bytes: &mut [u8]) -> crate::io::Result<()> { + use crate::fs::File; + use crate::io::Read; + use crate::sync::OnceLock; + + #[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "espidf", + target_os = "horizon", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "solaris", + target_os = "illumos", + netbsd10, + ))] + if let Some(res) = imp::syscall(bytes) { + return res; + } + + const PATH: &'static str = if cfg!(target_os = "redox") { + "/scheme/rand" + } else { + "/dev/urandom" + }; + + static FILE: OnceLock = OnceLock::new(); + + FILE.get_or_try_init(|| File::open(PATH))?.read_exact(bytes) + } + } +} + +// All these systems a `getrandom` syscall. +// +// It is not guaranteed to be available, so return None to fallback to the file +// implementation. +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "espidf", + target_os = "horizon", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "solaris", + target_os = "illumos", + netbsd10, ))] mod imp { - use crate::fs::File; - use crate::io::Read; - - #[cfg(any(target_os = "linux", target_os = "android"))] - use crate::sys::weak::syscall; + use crate::io::{Error, Result}; + use crate::sync::atomic::{AtomicBool, Ordering}; + use crate::sys::os::errno; #[cfg(any(target_os = "linux", target_os = "android"))] fn getrandom(buf: &mut [u8]) -> libc::ssize_t { - use crate::sync::atomic::{AtomicBool, Ordering}; - use crate::sys::os::errno; + use crate::sys::weak::syscall; // A weak symbol allows interposition, e.g. for perf measurements that want to // disable randomness for consistency. Otherwise, we'll try a raw syscall. @@ -60,6 +112,7 @@ mod imp { } #[cfg(any( + target_os = "dragonfly", target_os = "espidf", target_os = "horizon", target_os = "freebsd", @@ -71,51 +124,11 @@ mod imp { unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } } - #[cfg(target_os = "dragonfly")] - fn getrandom(buf: &mut [u8]) -> libc::ssize_t { - extern "C" { - fn getrandom( - buf: *mut libc::c_void, - buflen: libc::size_t, - flags: libc::c_uint, - ) -> libc::ssize_t; - } - unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } - } - - #[cfg(not(any( - target_os = "linux", - target_os = "android", - target_os = "espidf", - target_os = "horizon", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "solaris", - target_os = "illumos", - netbsd10 - )))] - fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool { - false - } - - #[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "espidf", - target_os = "horizon", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "solaris", - target_os = "illumos", - netbsd10 - ))] - fn getrandom_fill_bytes(v: &mut [u8]) -> bool { - use crate::sync::atomic::{AtomicBool, Ordering}; - use crate::sys::os::errno; - + pub fn syscall(v: &mut [u8]) -> Option> { static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false); + if GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed) { - return false; + return None; } let mut read = 0; @@ -126,8 +139,7 @@ mod imp { if err == libc::EINTR { continue; } else if err == libc::ENOSYS || err == libc::EPERM { - // Fall back to reading /dev/urandom if `getrandom` is not - // supported on the current kernel. + // `getrandom` is not supported on the current system. // // Also fall back in case it is disabled by something like // seccomp or inside of docker. @@ -143,122 +155,83 @@ mod imp { // https://github.com/moby/moby/issues/42680 // GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed); - return false; + return None; } else if err == libc::EAGAIN { - return false; + // getrandom has failed because it would have blocked as the + // non-blocking pool (urandom) has not been initialized in + // the kernel yet due to a lack of entropy. Fallback to + // reading from `/dev/urandom` which will return potentially + // insecure random data to avoid blocking applications which + // could depend on this call without ever knowing they do and + // don't have a work around. + return None; } else { - panic!("unexpected getrandom error: {err}"); + return Some(Err(Error::from_raw_os_error(err))); } } else { read += result as usize; } } - true - } - pub fn fill_bytes(v: &mut [u8]) { - // getrandom_fill_bytes here can fail if getrandom() returns EAGAIN, - // meaning it would have blocked because the non-blocking pool (urandom) - // has not initialized in the kernel yet due to a lack of entropy. The - // fallback we do here is to avoid blocking applications which could - // depend on this call without ever knowing they do and don't have a - // work around. The PRNG of /dev/urandom will still be used but over a - // possibly predictable entropy pool. - if getrandom_fill_bytes(v) { - return; - } - - // getrandom failed because it is permanently or temporarily (because - // of missing entropy) unavailable. Open /dev/urandom, read from it, - // and close it again. - let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom"); - file.read_exact(v).expect("failed to read /dev/urandom") + Some(Ok(())) } } -#[cfg(target_vendor = "apple")] +#[cfg(any( + target_os = "macos", // Supported since macOS 10.12+. + target_os = "openbsd", + target_os = "emscripten", + target_os = "vita", +))] mod imp { - use crate::io; - use libc::{c_int, c_void, size_t}; - - #[inline(always)] - fn random_failure() -> ! { - panic!("unexpected random generation error: {}", io::Error::last_os_error()); - } - - #[cfg(target_os = "macos")] - fn getentropy_fill_bytes(v: &mut [u8]) { - extern "C" { - fn getentropy(bytes: *mut c_void, count: size_t) -> c_int; - } + use crate::io::{Error, Result}; + pub fn syscall(v: &mut [u8]) -> Result<()> { // getentropy(2) permits a maximum buffer size of 256 bytes for s in v.chunks_mut(256) { - let ret = unsafe { getentropy(s.as_mut_ptr().cast(), s.len()) }; + let ret = unsafe { libc::getentropy(s.as_mut_ptr().cast(), s.len()) }; if ret == -1 { - random_failure() + return Err(Error::last_os_error()); } } - } - #[cfg(not(target_os = "macos"))] - fn ccrandom_fill_bytes(v: &mut [u8]) { - extern "C" { - fn CCRandomGenerateBytes(bytes: *mut c_void, count: size_t) -> c_int; - } - - let ret = unsafe { CCRandomGenerateBytes(v.as_mut_ptr().cast(), v.len()) }; - if ret == -1 { - random_failure() - } - } - - pub fn fill_bytes(v: &mut [u8]) { - // All supported versions of macOS (10.12+) support getentropy. - // - // `getentropy` is measurably faster (via Divan) then the other alternatives so its preferred - // when usable. - #[cfg(target_os = "macos")] - getentropy_fill_bytes(v); - - // On Apple platforms, `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply - // call into `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes` - // manages a CSPRNG which is seeded from the kernel's CSPRNG and which runs on - // its own thread accessed via GCD. This seems needlessly heavyweight for our purposes - // so we only use it on non-Mac OSes where the better entrypoints are blocked. - // - // `CCRandomGenerateBytes` is used instead of `SecRandomCopyBytes` because the former is accessible - // via `libSystem` (libc) while the other needs to link to `Security.framework`. - // - // Note that while `getentropy` has a available attribute in the macOS headers, the lack - // of a header in the iOS (and others) SDK means that its can cause app store rejections. - // Just use `CCRandomGenerateBytes` instead. - #[cfg(not(target_os = "macos"))] - ccrandom_fill_bytes(v); + Ok(()) } } -#[cfg(any(target_os = "openbsd", target_os = "emscripten", target_os = "vita"))] +// On Apple platforms, `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply +// call into `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes` +// manages a CSPRNG which is seeded from the kernel's CSPRNG and which runs on +// its own thread accessed via GCD. This seems needlessly heavyweight for our purposes +// so we only use it when `getentropy` is blocked, which appears to be the case +// on all platforms except macOS (see #102643). +// +// `CCRandomGenerateBytes` is used instead of `SecRandomCopyBytes` because the former is accessible +// via `libSystem` (libc) while the other needs to link to `Security.framework`. +#[cfg(all(target_vendor = "apple", not(target_os = "macos")))] mod imp { - use crate::sys::os::errno; + use libc::size_t; - pub fn fill_bytes(v: &mut [u8]) { - // getentropy(2) permits a maximum buffer size of 256 bytes - for s in v.chunks_mut(256) { - let ret = unsafe { libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len()) }; - if ret == -1 { - panic!("unexpected getentropy error: {}", errno()); - } + use crate::ffi::{c_int, c_void}; + use crate::io::{Error, Result}; + + pub fn syscall(v: &mut [u8]) -> Result<()> { + extern "C" { + fn CCRandomGenerateBytes(bytes: *mut c_void, count: size_t) -> c_int; } + + let ret = unsafe { CCRandomGenerateBytes(v.as_mut_ptr().cast(), v.len()) }; + if ret != -1 { Ok(()) } else { Err(Error::last_os_error()) } } } // FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification. #[cfg(all(target_os = "netbsd", not(netbsd10)))] mod imp { + use crate::io::{Error, Result}; use crate::ptr; - pub fn fill_bytes(v: &mut [u8]) { + pub fn syscall(v: &mut [u8]) -> Result<()> { let mib = [libc::CTL_KERN, libc::KERN_ARND]; // kern.arandom permits a maximum buffer size of 256 bytes for s in v.chunks_mut(256) { @@ -273,64 +246,57 @@ mod imp { 0, ) }; - if ret == -1 || s_len != s.len() { - panic!( - "kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})", - ret, - s.len(), - s_len - ); + if ret == -1 { + return Err(Error::last_os_error()); + } else if s_len != s.len() { + // FIXME(joboet): this can't actually happen, can it? + panic!("read less bytes than requested from kern.arandom"); } } + + Ok(()) } } #[cfg(target_os = "fuchsia")] mod imp { + use crate::io::Result; + #[link(name = "zircon")] extern "C" { fn zx_cprng_draw(buffer: *mut u8, len: usize); } - pub fn fill_bytes(v: &mut [u8]) { - unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) } - } -} - -#[cfg(target_os = "redox")] -mod imp { - use crate::fs::File; - use crate::io::Read; - - pub fn fill_bytes(v: &mut [u8]) { - // Open rand:, read from it, and close it again. - let mut file = File::open("rand:").expect("failed to open rand:"); - file.read_exact(v).expect("failed to read rand:") + pub fn syscall(v: &mut [u8]) -> Result<()> { + unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) }; + Ok(()) } } #[cfg(target_os = "vxworks")] mod imp { - use crate::io; - use core::sync::atomic::{AtomicBool, Ordering::Relaxed}; + use core::sync::atomic::AtomicBool; + use core::sync::atomic::Ordering::Relaxed; - pub fn fill_bytes(v: &mut [u8]) { + use crate::io::{Error, Result}; + + pub fn syscall(v: &mut [u8]) -> Result<()> { static RNG_INIT: AtomicBool = AtomicBool::new(false); while !RNG_INIT.load(Relaxed) { let ret = unsafe { libc::randSecure() }; if ret < 0 { - panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); + return Err(Error::last_os_error()); } else if ret > 0 { RNG_INIT.store(true, Relaxed); break; } + unsafe { libc::usleep(10) }; } + let ret = unsafe { libc::randABytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int) }; - if ret < 0 { - panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); - } + if ret >= 0 { Ok(()) } else { Err(Error::last_os_error()) } } } diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 2e5bd85327a19..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(); diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 619f4e4121e73..c9dcc5ad97a50 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"))] +#[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")] @@ -215,17 +212,31 @@ impl Thread { } } + #[cfg(target_os = "vxworks")] + pub fn set_name(name: &CStr) { + // FIXME(libc): adding real STATUS, ERROR type eventually. + extern "C" { + fn taskNameSet(task_id: libc::TASK_ID, task_name: *mut libc::c_char) -> libc::c_int; + } + + // VX_TASK_NAME_LEN is 31 in VxWorks 7. + const VX_TASK_NAME_LEN: usize = 31; + + let mut name = truncate_cstr::<{ VX_TASK_NAME_LEN }>(name); + let res = unsafe { taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; + debug_assert_eq!(res, libc::OK); + } + #[cfg(any( target_env = "newlib", target_os = "l4re", target_os = "emscripten", target_os = "redox", - target_os = "vxworks", target_os = "hurd", target_os = "aix", ))] pub fn set_name(_name: &CStr) { - // Newlib, Emscripten, and VxWorks have no way to set a thread name. + // Newlib and Emscripten have no way to set a thread name. } #[cfg(not(target_os = "espidf"))] @@ -256,23 +267,39 @@ impl Thread { #[cfg(target_os = "espidf")] pub fn sleep(dur: Duration) { - let mut micros = dur.as_micros(); - unsafe { - while micros > 0 { - let st = if micros > u32::MAX as u128 { u32::MAX } else { micros as u32 }; + // ESP-IDF does not have `nanosleep`, so we use `usleep` instead. + // As per the documentation of `usleep`, it is expected to support + // sleep times as big as at least up to 1 second. + // + // ESP-IDF does support almost up to `u32::MAX`, but due to a potential integer overflow in its + // `usleep` implementation + // (https://github.com/espressif/esp-idf/blob/d7ca8b94c852052e3bc33292287ef4dd62c9eeb1/components/newlib/time.c#L210), + // we limit the sleep time to the maximum one that would not cause the underlying `usleep` implementation to overflow + // (`portTICK_PERIOD_MS` can be anything between 1 to 1000, and is 10 by default). + const MAX_MICROS: u32 = u32::MAX - 1_000_000 - 1; + + // Add any nanoseconds smaller than a microsecond as an extra microsecond + // so as to comply with the `std::thread::sleep` contract which mandates + // implementations to sleep for _at least_ the provided `dur`. + // We can't overflow `micros` as it is a `u128`, while `Duration` is a pair of + // (`u64` secs, `u32` nanos), where the nanos are strictly smaller than 1 second + // (i.e. < 1_000_000_000) + let mut micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; + + while micros > 0 { + let st = if micros > MAX_MICROS as u128 { MAX_MICROS } else { micros as u32 }; + unsafe { libc::usleep(st); - - micros -= st as u128; } + + micros -= st as u128; } } 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 +307,7 @@ impl Thread { } pub fn into_id(self) -> libc::pthread_t { - let id = self.id; - mem::forget(self); - id + ManuallyDrop::new(self).id } } @@ -298,6 +323,7 @@ impl Drop for Thread { target_os = "nto", target_os = "solaris", target_os = "illumos", + target_os = "vxworks", target_vendor = "apple", ))] fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { @@ -462,8 +488,20 @@ 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; + } + + // SAFETY: `vxCpuEnabledGet` always fetches a mask with at least one bit set + unsafe{ + let set = 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")) } } @@ -479,11 +517,9 @@ mod cgroups { use crate::borrow::Cow; use crate::ffi::OsString; use crate::fs::{exists, File}; - use crate::io::Read; - use crate::io::{BufRead, BufReader}; + 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)] 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/alloc.rs b/library/std/src/sys/pal/unsupported/alloc.rs deleted file mode 100644 index d715ae45401e6..0000000000000 --- a/library/std/src/sys/pal/unsupported/alloc.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::ptr::null_mut; - -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { - null_mut() - } - - #[inline] - unsafe fn alloc_zeroed(&self, _layout: Layout) -> *mut u8 { - null_mut() - } - - #[inline] - unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} - - #[inline] - unsafe fn realloc(&self, _ptr: *mut u8, _layout: Layout, _new_size: usize) -> *mut u8 { - null_mut() - } -} diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index 442e6042ad561..01d516f7568bf 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -1,6 +1,5 @@ #![deny(unsafe_op_in_unsafe_fn)] -pub mod alloc; pub mod args; pub mod env; pub mod fs; diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs index 3be98898bbeb9..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 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/wasi/args.rs b/library/std/src/sys/pal/wasi/args.rs index c42c310e3a254..52cfa202af825 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)] +#![forbid(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/env.rs b/library/std/src/sys/pal/wasi/env.rs index 730e356d7fe95..8d44498267360 100644 --- a/library/std/src/sys/pal/wasi/env.rs +++ b/library/std/src/sys/pal/wasi/env.rs @@ -1,3 +1,5 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + pub mod os { pub const FAMILY: &str = ""; pub const OS: &str = ""; diff --git a/library/std/src/sys/pal/wasi/fd.rs b/library/std/src/sys/pal/wasi/fd.rs index 8966e4b80ad37..19b60157e2e00 100644 --- a/library/std/src/sys/pal/wasi/fd.rs +++ b/library/std/src/sys/pal/wasi/fd.rs @@ -1,4 +1,4 @@ -#![deny(unsafe_op_in_unsafe_fn)] +#![forbid(unsafe_op_in_unsafe_fn)] #![allow(dead_code)] use super::err2io; diff --git a/library/std/src/sys/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs index c58e6a08b374e..88b1e543ec7c2 100644 --- a/library/std/src/sys/pal/wasi/fs.rs +++ b/library/std/src/sys/pal/wasi/fs.rs @@ -1,23 +1,20 @@ -#![deny(unsafe_op_in_unsafe_fn)] +#![forbid(unsafe_op_in_unsafe_fn)] 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; -use crate::sys_common::{AsInner, FromInner, IntoInner}; - pub use crate::sys_common::fs::exists; +use crate::sys_common::{ignore_notfound, AsInner, FromInner, IntoInner}; +use crate::{fmt, iter, ptr}; pub struct File { fd: WasiFd, @@ -797,14 +794,22 @@ fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { io::const_io_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found") })?; - if entry.file_type()?.is_dir() { - remove_dir_all_recursive(&entry.inner.dir.fd, path.as_ref())?; - } else { - entry.inner.dir.fd.unlink_file(path)?; + let result: io::Result<()> = try { + if entry.file_type()?.is_dir() { + remove_dir_all_recursive(&entry.inner.dir.fd, path.as_ref())?; + } else { + entry.inner.dir.fd.unlink_file(path)?; + } + }; + // ignore internal NotFound errors + if let Err(err) = &result + && err.kind() != io::ErrorKind::NotFound + { + return result; } } // Once all this directory's contents are deleted it should be safe to // delete the directory tiself. - parent.remove_directory(osstr2str(path.as_ref())?) + ignore_notfound(parent.remove_directory(osstr2str(path.as_ref())?)) } diff --git a/library/std/src/sys/pal/wasi/helpers.rs b/library/std/src/sys/pal/wasi/helpers.rs index 82149cef8fad1..d047bf2fce857 100644 --- a/library/std/src/sys/pal/wasi/helpers.rs +++ b/library/std/src/sys/pal/wasi/helpers.rs @@ -1,5 +1,6 @@ -use crate::io as std_io; -use crate::mem; +#![forbid(unsafe_op_in_unsafe_fn)] + +use crate::{io as std_io, mem}; #[inline] pub fn is_interrupted(errno: i32) -> bool { diff --git a/library/std/src/sys/pal/wasi/io.rs b/library/std/src/sys/pal/wasi/io.rs index 2cd45df88fad1..b7c2f03daa048 100644 --- a/library/std/src/sys/pal/wasi/io.rs +++ b/library/std/src/sys/pal/wasi/io.rs @@ -1,4 +1,4 @@ -#![deny(unsafe_op_in_unsafe_fn)] +#![forbid(unsafe_op_in_unsafe_fn)] use crate::marker::PhantomData; use crate::os::fd::{AsFd, AsRawFd}; diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index d8fe06d1973c9..8051021a58897 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -14,8 +14,6 @@ //! compiling for wasm. That way it's a compile time error for something that's //! guaranteed to be a runtime error! -#[path = "../unix/alloc.rs"] -pub mod alloc; pub mod args; pub mod env; pub mod fd; @@ -48,8 +46,5 @@ mod helpers; // 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; 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/net.rs b/library/std/src/sys/pal/wasi/net.rs index b4cf94c8781ec..a648679982812 100644 --- a/library/std/src/sys/pal/wasi/net.rs +++ b/library/std/src/sys/pal/wasi/net.rs @@ -1,4 +1,4 @@ -#![deny(unsafe_op_in_unsafe_fn)] +#![forbid(unsafe_op_in_unsafe_fn)] use super::err2io; use super::fd::WasiFd; diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index e96296997e6a9..f7701360f5a9c 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)] +#![forbid(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 { diff --git a/library/std/src/sys/pal/wasi/stdio.rs b/library/std/src/sys/pal/wasi/stdio.rs index 4cc0e4ed5a45a..ca49f871e1957 100644 --- a/library/std/src/sys/pal/wasi/stdio.rs +++ b/library/std/src/sys/pal/wasi/stdio.rs @@ -1,4 +1,4 @@ -#![deny(unsafe_op_in_unsafe_fn)] +#![forbid(unsafe_op_in_unsafe_fn)] use super::fd::WasiFd; use crate::io::{self, IoSlice, IoSliceMut}; diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 975eef2451f4c..4b83870fdea6c 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -1,9 +1,10 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + 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")] { @@ -74,13 +75,13 @@ impl Thread { if #[cfg(target_feature = "atomics")] { 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); let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE); - 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); @@ -91,20 +92,20 @@ 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); } }; - let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _); + let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, p as *mut _) }; // 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)); + unsafe { drop(Box::from_raw(p)); } Err(io::Error::from_raw_os_error(ret)) } else { Ok(Thread { id: native }) @@ -135,36 +136,37 @@ impl Thread { } pub fn sleep(dur: Duration) { - let nanos = dur.as_nanos(); - assert!(nanos <= u64::MAX as u128); - - const USERDATA: wasi::Userdata = 0x0123_45678; - - let clock = wasi::SubscriptionClock { - id: wasi::CLOCKID_MONOTONIC, - timeout: nanos as u64, - precision: 0, - flags: 0, - }; - - let in_ = wasi::Subscription { - userdata: USERDATA, - u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, - }; - unsafe { - let mut event: wasi::Event = mem::zeroed(); - let res = wasi::poll_oneoff(&in_, &mut event, 1); - match (res, event) { - ( - Ok(1), - wasi::Event { - userdata: USERDATA, - error: wasi::ERRNO_SUCCESS, - type_: wasi::EVENTTYPE_CLOCK, - .. - }, - ) => {} - _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), + let mut nanos = dur.as_nanos(); + while nanos > 0 { + const USERDATA: wasi::Userdata = 0x0123_45678; + + let clock = wasi::SubscriptionClock { + id: wasi::CLOCKID_MONOTONIC, + timeout: u64::try_from(nanos).unwrap_or(u64::MAX), + precision: 0, + flags: 0, + }; + nanos -= u128::from(clock.timeout); + + let in_ = wasi::Subscription { + userdata: USERDATA, + u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, + }; + unsafe { + let mut event: wasi::Event = mem::zeroed(); + let res = wasi::poll_oneoff(&in_, &mut event, 1); + match (res, event) { + ( + Ok(1), + wasi::Event { + userdata: USERDATA, + error: wasi::ERRNO_SUCCESS, + type_: wasi::EVENTTYPE_CLOCK, + .. + }, + ) => {} + _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), + } } } } @@ -172,12 +174,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/wasi/time.rs b/library/std/src/sys/pal/wasi/time.rs index 016b06efbdc63..0d8d0b59ac14a 100644 --- a/library/std/src/sys/pal/wasi/time.rs +++ b/library/std/src/sys/pal/wasi/time.rs @@ -1,4 +1,4 @@ -#![deny(unsafe_op_in_unsafe_fn)] +#![forbid(unsafe_op_in_unsafe_fn)] use crate::time::Duration; diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 0930d2e22fa8d..546fadbe5011c 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -6,8 +6,6 @@ //! To begin with, this target mirrors the wasi target 1 to 1, but over //! time this will change significantly. -#[path = "../unix/alloc.rs"] -pub mod alloc; #[path = "../wasi/args.rs"] pub mod args; #[path = "../wasi/env.rs"] @@ -51,10 +49,7 @@ mod helpers; // 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; 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/atomics/futex.rs b/library/std/src/sys/pal/wasm/atomics/futex.rs index 3584138ca0447..42913a99ee9d6 100644 --- a/library/std/src/sys/pal/wasm/atomics/futex.rs +++ b/library/std/src/sys/pal/wasm/atomics/futex.rs @@ -24,7 +24,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. @@ -32,7 +32,7 @@ pub fn futex_wake(futex: &AtomicU32) -> bool { 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 { wasm::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, i32::MAX as u32); diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 4c34859e918bb..8141bfac49aad 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -16,7 +16,6 @@ #![deny(unsafe_op_in_unsafe_fn)] -pub mod alloc; #[path = "../unsupported/args.rs"] pub mod args; pub mod env; diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index 00c816a6c09b8..9e336ff2d473d 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -254,7 +254,7 @@ pub struct WinError { pub code: u32, } impl WinError { - const fn new(code: u32) -> Self { + pub const fn new(code: u32) -> Self { Self { code } } } @@ -272,8 +272,11 @@ impl WinError { // 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 BAD_NET_NAME: Self = Self::new(c::ERROR_BAD_NET_NAME); + pub const BAD_NETPATH: Self = Self::new(c::ERROR_BAD_NETPATH); 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 DIR_NOT_EMPTY: Self = Self::new(c::ERROR_DIR_NOT_EMPTY); 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); 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 296d19a926d96..b888eb7d95ca3 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -4,27 +4,15 @@ #![cfg_attr(test, allow(dead_code))] #![unstable(issue = "none", feature = "windows_c")] #![allow(clippy::style)] -#![allow(unsafe_op_in_unsafe_fn)] -use crate::ffi::CStr; -use crate::mem; -use crate::os::raw::{c_char, c_int, c_uint, c_ulong, c_ushort, c_void}; -use crate::os::windows::io::{AsRawHandle, BorrowedHandle}; -use crate::ptr; - -pub(super) mod windows_targets; +use core::ffi::{c_uint, c_ulong, c_ushort, c_void, CStr}; +use core::{mem, ptr}; mod windows_sys; pub use windows_sys::*; pub type WCHAR = u16; -pub type socklen_t = c_int; -pub type ADDRESS_FAMILY = c_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 _); // https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure?view=msvc-170 @@ -42,20 +30,6 @@ pub const INIT_ONCE_STATIC_INIT: INIT_ONCE = INIT_ONCE { Ptr: ptr::null_mut() }; 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 @@ -127,45 +101,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: [c_char; 6], - pub __ss_align: i64, - pub __ss_pad2: [c_char; 112], -} - -#[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, -} - -#[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"))] { @@ -173,139 +108,16 @@ 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: u32, - byteoffset: Option<&i64>, - key: Option<&u32>, -) -> 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: u32, - byteoffset: Option<&i64>, - key: Option<&u32>, -) -> 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"))] { - #[cfg(target_arch = "x86")] - #[link(name = "bcryptprimitives", kind = "raw-dylib", import_name_type = "undecorated")] - extern "system" { - pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; - } - #[cfg(not(target_arch = "x86"))] - #[link(name = "bcryptprimitives", kind = "raw-dylib")] - extern "system" { - pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; - } -}} +#[cfg(not(target_vendor = "win7"))] +#[cfg_attr( + target_arch = "x86", + link(name = "bcryptprimitives", kind = "raw-dylib", import_name_type = "undecorated") +)] +#[cfg_attr(not(target_arch = "x86"), link(name = "bcryptprimitives", kind = "raw-dylib"))] +extern "system" { + pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; +} // Functions that aren't available on every version of Windows that we support, // but we still use them and just provide some form of a fallback implementation. @@ -315,26 +127,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 u32); 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 u32); 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) } } } @@ -367,12 +179,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"))] @@ -419,36 +231,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, + apccontext: *const c_void, + iostatusblock: *mut IO_STATUS_BLOCK, + buffer: *mut c_void, length: u32, - byteoffset: Option<&i64>, - key: Option<&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, + apccontext: *const c_void, + iostatusblock: *mut IO_STATUS_BLOCK, + buffer: *const c_void, length: u32, - byteoffset: Option<&i64>, - key: Option<&u32> + byteoffset: *const i64, + key: *const u32 ) -> NTSTATUS { STATUS_NOT_IMPLEMENTED } @@ -457,44 +269,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 {} -}} -// WSAStartup is only redefined here so that we can override WSADATA for Arm32 -windows_targets::link!("ws2_32.dll" "system" 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/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index 5ad4a3731d822..9c2e4500da068 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -34,6 +34,7 @@ Windows.Wdk.Storage.FileSystem.FILE_WRITE_THROUGH Windows.Wdk.Storage.FileSystem.NtCreateFile Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_DISPOSITION Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_OPTIONS +Windows.Wdk.Storage.FileSystem.NtOpenFile Windows.Wdk.Storage.FileSystem.NtReadFile Windows.Wdk.Storage.FileSystem.NtWriteFile Windows.Wdk.Storage.FileSystem.SYMLINK_FLAG_RELATIVE @@ -1931,10 +1932,14 @@ Windows.Win32.Foundation.RtlNtStatusToDosError Windows.Win32.Foundation.SetHandleInformation Windows.Win32.Foundation.SetLastError Windows.Win32.Foundation.STATUS_DELETE_PENDING +Windows.Win32.Foundation.STATUS_DIRECTORY_NOT_EMPTY Windows.Win32.Foundation.STATUS_END_OF_FILE +Windows.Win32.Foundation.STATUS_FILE_DELETED +Windows.Win32.Foundation.STATUS_INVALID_HANDLE Windows.Win32.Foundation.STATUS_INVALID_PARAMETER Windows.Win32.Foundation.STATUS_NOT_IMPLEMENTED Windows.Win32.Foundation.STATUS_PENDING +Windows.Win32.Foundation.STATUS_SHARING_VIOLATION Windows.Win32.Foundation.STATUS_SUCCESS Windows.Win32.Foundation.TRUE Windows.Win32.Foundation.UNICODE_STRING @@ -2059,6 +2064,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 +2181,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 +2426,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 +2470,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 fea00fec9ae59..ab5f8919d7af6 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -5,6 +5,7 @@ windows_targets::link!("advapi32.dll" "system" fn OpenProcessToken(processhandle 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); @@ -14,6 +15,7 @@ windows_targets::link!("kernel32.dll" "system" fn CreateEventW(lpeventattributes 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); @@ -103,6 +105,7 @@ windows_targets::link!("kernel32.dll" "system" fn WideCharToMultiByte(codepage : 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 NtOpenFile(filehandle : *mut HANDLE, desiredaccess : u32, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : 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); @@ -113,6 +116,7 @@ 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); @@ -2283,6 +2287,12 @@ 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, @@ -2859,6 +2869,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; @@ -2890,6 +2902,14 @@ pub struct SOCKADDR { } #[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], @@ -2963,10 +2983,14 @@ pub struct STARTUPINFOW { } pub type STARTUPINFOW_FLAGS = u32; pub const STATUS_DELETE_PENDING: NTSTATUS = 0xC0000056_u32 as _; +pub const STATUS_DIRECTORY_NOT_EMPTY: NTSTATUS = 0xC0000101_u32 as _; pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _; +pub const STATUS_FILE_DELETED: NTSTATUS = 0xC0000123_u32 as _; +pub const STATUS_INVALID_HANDLE: NTSTATUS = 0xC0000008_u32 as _; pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xC000000D_u32 as _; pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002_u32 as _; pub const STATUS_PENDING: NTSTATUS = 0x103_u32 as _; +pub const STATUS_SHARING_VIOLATION: NTSTATUS = 0xC0000043_u32 as _; pub const STATUS_SUCCESS: NTSTATUS = 0x0_u32 as _; pub const STD_ERROR_HANDLE: STD_HANDLE = 4294967284u32; pub type STD_HANDLE = u32; @@ -3283,5 +3307,18 @@ pub struct XSAVE_FORMAT { pub XmmRegisters: [M128A; 8], pub Reserved4: [u8; 224], } + +#[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/compat.rs b/library/std/src/sys/pal/windows/compat.rs index 49fa1603f3e1e..75232dfc0b0f1 100644 --- a/library/std/src/sys/pal/windows/compat.rs +++ b/library/std/src/sys/pal/windows/compat.rs @@ -158,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 { @@ -182,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)] @@ -225,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 48c39392047f0..5b360640c4e67 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -1,26 +1,24 @@ 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 crate::{fmt, ptr, slice}; -use super::api::{self, WinError}; -use super::{to_u16s, IoResult}; -use crate::sys::path::maybe_verbatim; +mod remove_dir_all; +use remove_dir_all::remove_dir_all_iterative; pub struct File { handle: Handle, @@ -32,6 +30,7 @@ pub struct FileAttr { creation_time: c::FILETIME, last_access_time: c::FILETIME, last_write_time: c::FILETIME, + change_time: Option, file_size: u64, reparse_tag: u32, volume_serial_number: Option, @@ -377,6 +376,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), @@ -413,6 +413,10 @@ impl File { 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, @@ -631,7 +635,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(); @@ -645,6 +649,22 @@ impl File { Ok(info) } } + + /// Deletes the file, consuming the file handle to ensure the delete occurs + /// as immediately as possible. + /// This attempts to use `posix_delete` but falls back to `win32_delete` + /// if that is not supported by the filesystem. + #[allow(unused)] + fn delete(self) -> Result<(), WinError> { + // If POSIX delete is not supported for this filesystem then fallback to win32 delete. + match self.posix_delete() { + Err(WinError::INVALID_PARAMETER) + | Err(WinError::NOT_SUPPORTED) + | Err(WinError::INVALID_FUNCTION) => self.win32_delete(), + result => result, + } + } + /// Delete using POSIX semantics. /// /// Files will be deleted as soon as the handle is closed. This is supported @@ -653,21 +673,23 @@ impl File { /// /// If the operation is not supported for this filesystem or OS version /// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`. - fn posix_delete(&self) -> io::Result<()> { + #[allow(unused)] + fn posix_delete(&self) -> Result<(), WinError> { let info = c::FILE_DISPOSITION_INFO_EX { Flags: c::FILE_DISPOSITION_FLAG_DELETE | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE, }; - api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info) } /// Delete a file using win32 semantics. The file won't actually be deleted /// until all file handles are closed. However, marking a file for deletion /// will prevent anyone from opening a new handle to the file. - fn win32_delete(&self) -> io::Result<()> { + #[allow(unused)] + fn win32_delete(&self) -> Result<(), WinError> { let info = c::FILE_DISPOSITION_INFO { DeleteFile: c::TRUE as _ }; - api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result() + api::set_file_information_by_handle(self.handle.as_raw_handle(), &info) } /// Fill the given buffer with as many directory entries as will fit. @@ -683,21 +705,23 @@ impl File { /// A symlink directory is simply an empty directory with some "reparse" metadata attached. /// So if you open a link (not its target) and iterate the directory, /// you will always iterate an empty directory regardless of the target. - fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> io::Result { + #[allow(unused)] + fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> Result { let class = if restart { c::FileIdBothDirectoryRestartInfo } else { c::FileIdBothDirectoryInfo }; unsafe { - let result = cvt(c::GetFileInformationByHandleEx( - self.handle.as_raw_handle(), + let result = c::GetFileInformationByHandleEx( + self.as_raw_handle(), class, buffer.as_mut_ptr().cast(), buffer.capacity() as _, - )); - match result { - Ok(_) => Ok(true), - Err(e) if e.raw_os_error() == Some(c::ERROR_NO_MORE_FILES as _) => Ok(false), - Err(e) => Err(e), + ); + if result == 0 { + let err = api::get_last_error(); + if err.code == c::ERROR_NO_MORE_FILES { Ok(false) } else { Err(err) } + } else { + Ok(true) } } } @@ -803,62 +827,6 @@ unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> } } -/// Open a link relative to the parent directory, ensure no symlinks are followed. -fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result { - // This is implemented using the lower level `NtCreateFile` function as - // unfortunately opening a file relative to a parent is not supported by - // win32 functions. It is however a fundamental feature of the NT kernel. - // - // See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile - unsafe { - let mut handle = ptr::null_mut(); - let mut io_status = c::IO_STATUS_BLOCK::PENDING; - let mut name_str = c::UNICODE_STRING::from_ref(name); - use crate::sync::atomic::{AtomicU32, Ordering}; - // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been - // tricked into following a symlink. However, it may not be available in - // earlier versions of Windows. - static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE); - let object = c::OBJECT_ATTRIBUTES { - ObjectName: &mut name_str, - RootDirectory: parent.as_raw_handle(), - Attributes: ATTRIBUTES.load(Ordering::Relaxed), - ..c::OBJECT_ATTRIBUTES::default() - }; - let status = c::NtCreateFile( - &mut handle, - access, - &object, - &mut io_status, - crate::ptr::null_mut(), - 0, - c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE, - c::FILE_OPEN, - // If `name` is a symlink then open the link rather than the target. - c::FILE_OPEN_REPARSE_POINT, - crate::ptr::null_mut(), - 0, - ); - // Convert an NTSTATUS to the more familiar Win32 error codes (aka "DosError") - if c::nt_success(status) { - Ok(File::from_raw_handle(handle)) - } else if status == c::STATUS_DELETE_PENDING { - // 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 i32)) - } else if status == c::STATUS_INVALID_PARAMETER - && ATTRIBUTES.load(Ordering::Relaxed) == c::OBJ_DONT_REPARSE - { - // Try without `OBJ_DONT_REPARSE`. See above. - ATTRIBUTES.store(0, Ordering::Relaxed); - open_link_no_reparse(parent, name, access) - } else { - Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as _)) - } - } -} - impl AsInner for File { #[inline] fn as_inner(&self) -> &Handle { @@ -957,6 +925,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 } @@ -976,6 +948,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 @@ -1136,114 +1109,22 @@ pub fn rmdir(p: &Path) -> io::Result<()> { Ok(()) } -/// Open a file or directory without following symlinks. -fn open_link(path: &Path, access_mode: u32) -> io::Result { +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + // Open a file or directory without following symlinks. let mut opts = OpenOptions::new(); - opts.access_mode(access_mode); + opts.access_mode(c::FILE_LIST_DIRECTORY); // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories. // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target. opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); - File::open(path, &opts) -} - -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - let file = open_link(path, c::DELETE | c::FILE_LIST_DIRECTORY)?; + let file = File::open(path, &opts)?; // Test if the file is not a directory or a symlink to a directory. if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 { return Err(io::Error::from_raw_os_error(c::ERROR_DIRECTORY as _)); } - match remove_dir_all_iterative(&file, File::posix_delete) { - Err(e) => { - if let Some(code) = e.raw_os_error() { - match code as u32 { - // If POSIX delete is not supported for this filesystem then fallback to win32 delete. - c::ERROR_NOT_SUPPORTED - | c::ERROR_INVALID_FUNCTION - | c::ERROR_INVALID_PARAMETER => { - remove_dir_all_iterative(&file, File::win32_delete) - } - _ => Err(e), - } - } else { - Err(e) - } - } - ok => ok, - } -} - -fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io::Result<()> { - // When deleting files we may loop this many times when certain error conditions occur. - // This allows remove_dir_all to succeed when the error is temporary. - const MAX_RETRIES: u32 = 10; - - let mut buffer = DirBuff::new(); - let mut dirlist = vec![f.duplicate()?]; - - // FIXME: This is a hack so we can push to the dirlist vec after borrowing from it. - fn copy_handle(f: &File) -> mem::ManuallyDrop { - unsafe { mem::ManuallyDrop::new(File::from_raw_handle(f.as_raw_handle())) } - } - - let mut restart = true; - while let Some(dir) = dirlist.last() { - let dir = copy_handle(dir); - - // Fill the buffer and iterate the entries. - let more_data = dir.fill_dir_buff(&mut buffer, restart)?; - restart = false; - for (name, is_directory) in buffer.iter() { - if is_directory { - let child_dir = open_link_no_reparse( - &dir, - &name, - c::SYNCHRONIZE | c::DELETE | c::FILE_LIST_DIRECTORY, - ); - // On success, add the handle to the queue. - // If opening the directory fails we treat it the same as a file - if let Ok(child_dir) = child_dir { - dirlist.push(child_dir); - continue; - } - } - for i in 1..=MAX_RETRIES { - let result = open_link_no_reparse(&dir, &name, c::SYNCHRONIZE | c::DELETE); - match result { - Ok(f) => delete(&f)?, - // Already deleted, so skip. - Err(e) if e.kind() == io::ErrorKind::NotFound => break, - // Retry a few times if the file is locked or a delete is already in progress. - Err(e) - if i < MAX_RETRIES - && (e.raw_os_error() == Some(c::ERROR_DELETE_PENDING as _) - || e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as _)) => {} - // Otherwise return the error. - Err(e) => return Err(e), - } - thread::yield_now(); - } - } - // If there were no more files then delete the directory. - if !more_data { - if let Some(dir) = dirlist.pop() { - // Retry deleting a few times in case we need to wait for a file to be deleted. - for i in 1..=MAX_RETRIES { - let result = delete(&dir); - if let Err(e) = result { - if i == MAX_RETRIES || e.kind() != io::ErrorKind::DirectoryNotEmpty { - return Err(e); - } - thread::yield_now(); - } else { - break; - } - } - } - } - } - Ok(()) + // Remove the directory and all its contents. + remove_dir_all_iterative(file).io_result() } pub fn readlink(path: &Path) -> io::Result { diff --git a/library/std/src/sys/pal/windows/fs/remove_dir_all.rs b/library/std/src/sys/pal/windows/fs/remove_dir_all.rs new file mode 100644 index 0000000000000..e7234ed8e5f56 --- /dev/null +++ b/library/std/src/sys/pal/windows/fs/remove_dir_all.rs @@ -0,0 +1,196 @@ +//! The Windows implementation of std::fs::remove_dir_all. +//! +//! This needs to address two issues: +//! +//! - It must not be possible to trick this into deleting files outside of +//! the parent directory (see CVE-2022-21658). +//! - It should not fail if many threads or processes call `remove_dir_all` +//! on the same path. +//! +//! The first is handled by using the low-level `NtOpenFile` API to open a file +//! relative to a parent directory. +//! +//! The second is trickier. Deleting a file works by setting its "disposition" +//! to delete. However, it isn't actually deleted until the file is closed. +//! During the gap between these two events, the file is in a kind of limbo +//! state where it still exists in the filesystem but anything trying to open +//! it fails with an error. +//! +//! The mitigations we use here are: +//! +//! - When attempting to open the file, we treat ERROR_DELETE_PENDING as a +//! successful delete. +//! - If the file still hasn't been removed from the filesystem by the time we +//! attempt to delete the parent directory, we try to wait for it to finish. +//! We can't wait indefinitely though so after some number of spins, we give +//! up and return an error. +//! +//! In short, we can't guarantee this will always succeed in the event of a +//! race but we do make a best effort such that it *should* do so. + +use core::ptr; +use core::sync::atomic::{AtomicU32, Ordering}; + +use super::{AsRawHandle, DirBuff, File, FromRawHandle}; +use crate::sys::c; +use crate::sys::pal::windows::api::WinError; +use crate::thread; + +// The maximum number of times to spin when waiting for deletes to complete. +const MAX_RETRIES: usize = 50; + +/// A wrapper around a raw NtOpenFile call. +/// +/// This isn't completely safe because `OBJECT_ATTRIBUTES` contains raw pointers. +unsafe fn nt_open_file( + access: u32, + object_attribute: &c::OBJECT_ATTRIBUTES, + share: u32, + options: u32, +) -> Result { + unsafe { + let mut handle = ptr::null_mut(); + let mut io_status = c::IO_STATUS_BLOCK::PENDING; + let status = + c::NtOpenFile(&mut handle, access, object_attribute, &mut io_status, share, options); + if c::nt_success(status) { + Ok(File::from_raw_handle(handle)) + } else { + // Convert an NTSTATUS to the more familiar Win32 error code (aka "DosError") + let win_error = if status == c::STATUS_DELETE_PENDING { + // We make a special exception for `STATUS_DELETE_PENDING` because + // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is + // very unhelpful because that can also mean a permission error. + WinError::DELETE_PENDING + } else { + WinError::new(c::RtlNtStatusToDosError(status)) + }; + Err(win_error) + } + } +} + +/// Open the file `path` in the directory `parent`, requesting the given `access` rights. +fn open_link_no_reparse( + parent: &File, + path: &[u16], + access: u32, +) -> Result, WinError> { + // This is implemented using the lower level `NtOpenFile` function as + // unfortunately opening a file relative to a parent is not supported by + // win32 functions. + // + // See https://learn.microsoft.com/windows/win32/api/winternl/nf-winternl-ntopenfile + + // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been + // tricked into following a symlink. However, it may not be available in + // earlier versions of Windows. + static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE); + + let result = unsafe { + let mut path_str = c::UNICODE_STRING::from_ref(path); + let mut object = c::OBJECT_ATTRIBUTES { + ObjectName: &mut path_str, + RootDirectory: parent.as_raw_handle(), + Attributes: ATTRIBUTES.load(Ordering::Relaxed), + ..c::OBJECT_ATTRIBUTES::default() + }; + let share = c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE; + let options = c::FILE_OPEN_REPARSE_POINT; + let result = nt_open_file(access, &object, share, options); + + // Retry without OBJ_DONT_REPARSE if it's not supported. + if matches!(result, Err(WinError::INVALID_PARAMETER)) + && ATTRIBUTES.load(Ordering::Relaxed) == c::OBJ_DONT_REPARSE + { + ATTRIBUTES.store(0, Ordering::Relaxed); + object.Attributes = 0; + nt_open_file(access, &object, share, options) + } else { + result + } + }; + + // Ignore not found errors + match result { + Ok(f) => Ok(Some(f)), + Err( + WinError::FILE_NOT_FOUND + | WinError::PATH_NOT_FOUND + | WinError::BAD_NETPATH + | WinError::BAD_NET_NAME + // `DELETE_PENDING` means something else is already trying to delete it + // so we assume that will eventually succeed. + | WinError::DELETE_PENDING, + ) => Ok(None), + Err(e) => Err(e), + } +} + +fn open_dir(parent: &File, name: &[u16]) -> Result, WinError> { + open_link_no_reparse(parent, name, c::SYNCHRONIZE | c::FILE_LIST_DIRECTORY) +} + +fn delete(parent: &File, name: &[u16]) -> Result<(), WinError> { + // Note that the `delete` function consumes the opened file to ensure it's + // dropped immediately. See module comments for why this is important. + match open_link_no_reparse(parent, name, c::SYNCHRONIZE | c::DELETE) { + Ok(Some(f)) => f.delete(), + Ok(None) => Ok(()), + Err(e) => Err(e), + } +} + +/// A simple retry loop that keeps running `f` while it fails with the given +/// error code or until `MAX_RETRIES` is reached. +fn retry( + mut f: impl FnMut() -> Result, + ignore: WinError, +) -> Result { + let mut i = MAX_RETRIES; + loop { + i -= 1; + if i == 0 { + return f(); + } else { + let result = f(); + if result != Err(ignore) { + return result; + } + } + thread::yield_now(); + } +} + +pub fn remove_dir_all_iterative(dir: File) -> Result<(), WinError> { + let mut buffer = DirBuff::new(); + let mut dirlist = vec![dir]; + + let mut restart = true; + 'outer: while let Some(dir) = dirlist.pop() { + let more_data = dir.fill_dir_buff(&mut buffer, restart)?; + for (name, is_directory) in buffer.iter() { + if is_directory { + let Some(subdir) = open_dir(&dir, &name)? else { continue }; + dirlist.push(dir); + dirlist.push(subdir); + continue 'outer; + } else { + // Attempt to delete, retrying on sharing violation errors as these + // can often be very temporary. E.g. if something takes just a + // bit longer than expected to release a file handle. + retry(|| delete(&dir, &name), WinError::SHARING_VIOLATION)?; + } + } + if more_data { + dirlist.push(dir); + restart = false; + } else { + // Attempt to delete, retrying on not empty errors because we may + // need to wait some time for files to be removed from the filesystem. + retry(|| delete(&dir, &[]), WinError::DIR_NOT_EMPTY)?; + restart = true; + } + } + Ok(()) +} diff --git a/library/std/src/sys/pal/windows/futex.rs b/library/std/src/sys/pal/windows/futex.rs index c54810e06cdd6..8c5081a607aa3 100644 --- a/library/std/src/sys/pal/windows/futex.rs +++ b/library/std/src/sys/pal/windows/futex.rs @@ -1,14 +1,13 @@ -use super::api::{self, WinError}; -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; diff --git a/library/std/src/sys/pal/windows/handle.rs b/library/std/src/sys/pal/windows/handle.rs index aaa1831dcc24d..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 { @@ -136,6 +136,12 @@ 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 [mem::MaybeUninit], @@ -243,15 +249,15 @@ impl Handle { // the provided `len`. let status = unsafe { c::NtReadFile( - self.as_handle(), + self.as_raw_handle(), ptr::null_mut(), None, ptr::null_mut(), &mut io_status, - buf, + buf.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(), ) }; @@ -293,15 +299,15 @@ impl Handle { 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 bf3dfdfdd3e7d..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)] diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index b85a8318bcbbd..1cc9a2b7ffa98 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -1,6 +1,7 @@ #![allow(missing_docs, nonstandard_style)] -#![deny(unsafe_op_in_unsafe_fn)] +#![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; @@ -9,14 +10,11 @@ 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; -mod api; +pub mod api; -pub mod alloc; pub mod args; pub mod c; pub mod env; diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs index d51fb56238f2c..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 @@ -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 @@ -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))) } diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index f1f4d3a5d26ef..5242bc9da31fe 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -5,20 +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::{self, WinError}; -use super::to_u16s; +use crate::{fmt, io, ptr, slice}; pub fn errno() -> i32 { api::get_last_error().code as i32 diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 7a309b9638bd2..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::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 @@ -181,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)?; @@ -221,15 +218,6 @@ fn random_number() -> usize { } } -// Abstracts over `ReadFileEx` and `WriteFileEx` -type AlertableIoFn = unsafe extern "system" fn( - BorrowedHandle<'_>, - *mut core::ffi::c_void, - u32, - *mut c::OVERLAPPED, - c::LPOVERLAPPED_COMPLETION_ROUTINE, -) -> c::BOOL; - impl AnonPipe { pub fn handle(&self) -> &Handle { &self.inner @@ -237,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(), u32::MAX as usize) as u32; - self.alertable_io_internal(c::ReadFileEx, buf.as_mut_ptr() as _, len) + 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,7 +252,10 @@ impl AnonPipe { pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { let result = unsafe { let len = crate::cmp::min(buf.capacity(), u32::MAX as usize) as u32; - self.alertable_io_internal(c::ReadFileEx, buf.as_mut().as_mut_ptr() as _, len) + 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,7 +290,9 @@ impl AnonPipe { pub fn write(&self, buf: &[u8]) -> io::Result { unsafe { let len = crate::cmp::min(buf.len(), u32::MAX as usize) as u32; - self.alertable_io_internal(c::WriteFileEx, buf.as_ptr() as _, len) + self.alertable_io_internal(|overlapped, callback| { + c::WriteFileEx(self.inner.as_raw_handle(), buf.as_ptr(), len, overlapped, callback) + }) } } @@ -323,12 +320,9 @@ impl AnonPipe { /// [`ReadFileEx`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfileex /// [`WriteFileEx`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefileex /// [Asynchronous Procedure Call]: https://docs.microsoft.com/en-us/windows/win32/sync/asynchronous-procedure-calls - #[allow(unsafe_op_in_unsafe_fn)] unsafe fn alertable_io_internal( &self, - io: AlertableIoFn, - buf: *mut core::ffi::c_void, - len: u32, + 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; } diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs index 76d2cb77d474c..d40a537e3594a 100644 --- a/library/std/src/sys/pal/windows/process.rs +++ b/library/std/src/sys/pal/windows/process.rs @@ -3,35 +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, EXIT_FAILURE, EXIT_SUCCESS}; -use crate::sys::cvt; 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 super::api::{self, WinError}; +use crate::{cmp, env, fmt, mem, ptr}; //////////////////////////////////////////////////////////////////////////////// // Command @@ -279,11 +272,24 @@ impl Command { None }; let program = resolve_exe(&self.program, || env::var_os("PATH"), child_paths)?; - // Case insensitive "ends_with" of UTF-16 encoded ".bat" or ".cmd" - let is_batch_file = matches!( - program.len().checked_sub(5).and_then(|i| program.get(i..)), - Some([46, 98 | 66, 97 | 65, 116 | 84, 0] | [46, 99 | 67, 109 | 77, 100 | 68, 0]) - ); + let has_bat_extension = |program: &[u16]| { + matches!( + // Case insensitive "ends_with" of UTF-16 encoded ".bat" or ".cmd" + program.len().checked_sub(4).and_then(|i| program.get(i..)), + Some([46, 98 | 66, 97 | 65, 116 | 84] | [46, 99 | 67, 109 | 77, 100 | 68]) + ) + }; + let is_batch_file = if path::is_verbatim(&program) { + has_bat_extension(&program[..program.len() - 1]) + } else { + super::fill_utf16_buf( + |buffer, size| unsafe { + // resolve the path so we can test the final file name. + c::GetFullPathNameW(program.as_ptr(), size, buffer, ptr::null_mut()) + }, + |program| has_bat_extension(program), + )? + }; let (program, mut cmd_str) = if is_batch_file { ( command_prompt()?, @@ -549,7 +555,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()?; @@ -571,7 +577,7 @@ impl Stdio { 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. 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/stdio.rs b/library/std/src/sys/pal/windows/stdio.rs index c6a21665157d7..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 core::str::utf8_char_width; + use super::api::{self, WinError}; -use crate::cmp; -use crate::io; 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; @@ -97,7 +94,7 @@ fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> i 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; } } @@ -246,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; } } diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index 668a3c05e20be..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; diff --git a/library/std/src/sys/pal/windows/time.rs b/library/std/src/sys/pal/windows/time.rs index b853daeffebd7..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; @@ -166,8 +165,7 @@ 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; @@ -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/mod.rs b/library/std/src/sys/pal/xous/mod.rs index 961d45c5e834f..b211e94db65d6 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -1,6 +1,5 @@ #![forbid(unsafe_op_in_unsafe_fn)] -pub mod alloc; #[path = "../unsupported/args.rs"] pub mod args; #[path = "../unsupported/env.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 9be09eed62989..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")] diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs index 279f24f9ee8e4..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, 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/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 651f25d66236b..20fdb7468a40d 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -10,7 +10,7 @@ const WORD_SIZE: usize = core::mem::size_of::(); -pub mod alloc; +pub mod abi; #[path = "../zkvm/args.rs"] pub mod args; pub mod env; @@ -26,13 +26,10 @@ pub mod pipe; #[path = "../unsupported/process.rs"] pub mod process; pub mod stdio; -#[path = "../unsupported/time.rs"] -pub mod time; - #[path = "../unsupported/thread.rs"] pub mod thread; - -mod abi; +#[path = "../unsupported/time.rs"] +pub mod time; use crate::io as std_io; diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index e7d6cd52a258e..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 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/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..2ae9a0a91996f 100644 --- a/library/std/src/sys/path/windows.rs +++ b/library/std/src/sys/path/windows.rs @@ -1,8 +1,8 @@ use crate::ffi::{OsStr, OsString}; -use crate::io; use crate::path::{Path, PathBuf, Prefix}; -use crate::ptr; +use crate::sys::api::utf16; use crate::sys::pal::{c, fill_utf16_buf, os2path, to_u16s}; +use crate::{io, ptr}; #[cfg(test)] mod tests; @@ -20,6 +20,10 @@ pub fn is_verbatim_sep(b: u8) -> bool { b == b'\\' } +pub fn is_verbatim(path: &[u16]) -> bool { + path.starts_with(utf16!(r"\\?\")) || path.starts_with(utf16!(r"\??\")) +} + /// Returns true if `path` looks like a lone filename. pub(crate) fn is_file_name(path: &OsStr) -> bool { !path.as_encoded_bytes().iter().copied().any(is_sep_byte) @@ -218,7 +222,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 89f7f133e21b4..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,7 +18,6 @@ pub struct DwarfReader { pub ptr: *const u8, } -#[forbid(unsafe_op_in_unsafe_fn)] impl DwarfReader { pub fn new(ptr: *const u8) -> DwarfReader { DwarfReader { ptr } 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/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 1a6ea1dafcb53..68085d026c40a 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -31,7 +31,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re")), + all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems")), all(target_vendor = "fortanix", target_env = "sgx"), ))] { mod gcc; 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 3a3039889e98b..f6f2b698e4945 100644 --- a/library/std/src/sys/sync/condvar/itron.rs +++ b/library/std/src/sys/sync/condvar/itron.rs @@ -1,9 +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 6457da91c2a5d..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}; 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/itron.rs b/library/std/src/sys/sync/mutex/itron.rs index 4ba32a8fbcd69..8440ffdd33772 100644 --- a/library/std/src/sys/sync/mutex/itron.rs +++ b/library/std/src/sys/sync/mutex/itron.rs @@ -1,18 +1,17 @@ //! Mutex implementation backed by μITRON mutexes. Assumes `acre_mtx` and //! `TA_INHERIT` are available. +#![forbid(unsafe_op_in_unsafe_fn)] -use crate::sys::pal::itron::{ - abi, - error::{expect_success, expect_success_aborting, fail, ItronError}, - spin::SpinIdOnceCell, -}; +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 { @@ -30,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/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 8a231e65ad134..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 7558eee8edd33..0537140202091 100644 --- a/library/std/src/sys/sync/rwlock/solid.rs +++ b/library/std/src/sys/sync/rwlock/solid.rs @@ -1,12 +1,9 @@ //! A readers-writer lock implementation backed by the SOLID kernel extension. +#![forbid(unsafe_op_in_unsafe_fn)] -use crate::sys::pal::{ - abi, - itron::{ - error::{expect_success, expect_success_aborting, fail, ItronError}, - spin::SpinIdOnceCell, - }, -}; +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 @@ -27,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 034eececb2a28..ce852eaadc4d9 100644 --- a/library/std/src/sys/sync/thread_parking/futex.rs +++ b/library/std/src/sys/sync/thread_parking/futex.rs @@ -36,7 +36,7 @@ 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) { unsafe { parker.write(Self { state: Atomic::new(EMPTY) }) }; 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/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/windows7.rs b/library/std/src/sys/sync/thread_parking/windows7.rs index 3a8d40dc5cfac..1000b63b6d01a 100644 --- a/library/std/src/sys/sync/thread_parking/windows7.rs +++ b/library/std/src/sys/sync/thread_parking/windows7.rs @@ -57,14 +57,13 @@ // [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; -use core::ffi::c_void; pub struct Parker { state: AtomicI8, @@ -95,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) }); @@ -185,16 +184,15 @@ impl Parker { #[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/guard/solid.rs b/library/std/src/sys/thread_local/guard/solid.rs index b65d00c5b5fb7..054b2d561c8b4 100644 --- a/library/std/src/sys/thread_local/guard/solid.rs +++ b/library/std/src/sys/thread_local/guard/solid.rs @@ -3,7 +3,8 @@ //! destructors for terminated tasks, we still keep our own list. use crate::cell::Cell; -use crate::sys::pal::{abi, itron::task}; +use crate::sys::pal::abi; +use crate::sys::pal::itron::task; use crate::sys::thread_local::destructors; pub fn enable() { diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs index f6cd457046ffc..bf94f7d6e3d13 100644 --- a/library/std/src/sys/thread_local/guard/windows.rs +++ b/library/std/src/sys/thread_local/guard/windows.rs @@ -63,9 +63,10 @@ //! [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; -use core::ffi::c_void; pub fn enable() { // When destructors are used, we don't want LLVM eliminating CALLBACK for any @@ -78,19 +79,6 @@ pub fn enable() { 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) { - // 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; - } - - unsafe { - ptr::from_ref(&_tls_used).read_volatile(); - } - } - if dw_reason == c::DLL_THREAD_DETACH || dw_reason == c::DLL_PROCESS_DETACH { #[cfg(target_thread_local)] unsafe { diff --git a/library/std/src/sys/thread_local/key/windows.rs b/library/std/src/sys/thread_local/key/windows.rs index 8b43e558d5d98..f4e0f25a476ee 100644 --- a/library/std/src/sys/thread_local/key/windows.rs +++ b/library/std/src/sys/thread_local/key/windows.rs @@ -26,10 +26,8 @@ use crate::cell::UnsafeCell; use crate::ptr; -use crate::sync::atomic::{ - AtomicPtr, AtomicU32, - Ordering::{AcqRel, Acquire, Relaxed, Release}, -}; +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; diff --git a/library/std/src/sys/thread_local/key/xous.rs b/library/std/src/sys/thread_local/key/xous.rs index 5a837a33e190e..4fb2fdcc61925 100644 --- a/library/std/src/sys/thread_local/key/xous.rs +++ b/library/std/src/sys/thread_local/key/xous.rs @@ -36,14 +36,13 @@ // FIXME(joboet): implement support for native TLS instead. -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}; 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}; pub type Key = usize; pub type Dtor = unsafe extern "C" fn(*mut u8); @@ -79,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(); diff --git a/library/std/src/sys/thread_local/native/eager.rs b/library/std/src/sys/thread_local/native/eager.rs index 99e5ae7fb9687..fd48c4f720216 100644 --- a/library/std/src/sys/thread_local/native/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::destructors; +use crate::sys::thread_local::{abort_on_dtor_unwind, destructors}; #[derive(Clone, Copy)] enum State { @@ -21,7 +20,7 @@ impl Storage { Storage { state: Cell::new(State::Initial), val: UnsafeCell::new(val) } } - /// Get a pointer to the TLS value. If the TLS variable has been destroyed, + /// Gets a pointer to the TLS value. If the TLS variable has been destroyed, /// a null pointer is returned. /// /// The resulting pointer may not be used after thread destruction has diff --git a/library/std/src/sys/thread_local/native/lazy.rs b/library/std/src/sys/thread_local/native/lazy.rs index 9d47e8ef68975..51294285ba013 100644 --- a/library/std/src/sys/thread_local/native/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::destructors; +use crate::sys::thread_local::{abort_on_dtor_unwind, destructors}; pub unsafe trait DestroyedState: Sized { fn register_dtor(s: &Storage); @@ -39,7 +38,7 @@ where Storage { state: UnsafeCell::new(State::Initial) } } - /// Get a pointer to the TLS value, potentially initializing it with the + /// 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. /// diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index e06185f00690b..e27b47c3f4536 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -60,7 +60,7 @@ impl Storage { Storage { key: LazyKey::new(Some(destroy_value::)), marker: PhantomData } } - /// Get a pointer to the TLS value, potentially initializing it with the + /// 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. /// diff --git a/library/std/src/sys/thread_local/statik.rs b/library/std/src/sys/thread_local/statik.rs index 0f08cab1ae4ff..a3451ab74e04f 100644 --- a/library/std/src/sys/thread_local/statik.rs +++ b/library/std/src/sys/thread_local/statik.rs @@ -63,7 +63,7 @@ impl LazyStorage { LazyStorage { value: UnsafeCell::new(None) } } - /// Get a pointer to the TLS value, potentially initializing it with the + /// Gets a pointer to the TLS value, potentially initializing it with the /// provided parameters. /// /// The resulting pointer may not be used after reentrant inialialization diff --git a/library/std/src/sys_common/fs.rs b/library/std/src/sys_common/fs.rs index acb6713cf1b14..a25a7244660bb 100644 --- a/library/std/src/sys_common/fs.rs +++ b/library/std/src/sys_common/fs.rs @@ -3,6 +3,7 @@ use crate::fs; use crate::io::{self, Error, ErrorKind}; use crate::path::Path; +use crate::sys_common::ignore_notfound; pub(crate) const NOT_FILE_ERROR: Error = io::const_io_error!( ErrorKind::InvalidInput, @@ -32,14 +33,22 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> { fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { for child in fs::read_dir(path)? { - let child = child?; - if child.file_type()?.is_dir() { - remove_dir_all_recursive(&child.path())?; - } else { - fs::remove_file(&child.path())?; + let result: io::Result<()> = try { + let child = child?; + if child.file_type()?.is_dir() { + remove_dir_all_recursive(&child.path())?; + } else { + fs::remove_file(&child.path())?; + } + }; + // ignore internal NotFound errors to prevent race conditions + if let Err(err) = &result + && err.kind() != io::ErrorKind::NotFound + { + return result; } } - fs::remove_dir(path) + ignore_notfound(fs::remove_dir(path)) } pub fn exists(path: &Path) -> io::Result { 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 60ee405ecaaa2..1c884f107beeb 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -80,3 +80,11 @@ pub fn mul_div_u64(value: u64, numer: u64, denom: u64) -> u64 { // r < denom, so (denom*numer) is the upper bound of (r*numer) q * numer + r * numer / denom } + +pub fn ignore_notfound(result: crate::io::Result) -> crate::io::Result<()> { + match result { + Err(err) if err.kind() == crate::io::ErrorKind::NotFound => Ok(()), + Ok(_) => Ok(()), + Err(err) => Err(err), + } +} 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/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 6aeeb6259285d..063451ad54e1c 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -19,20 +19,18 @@ mod tests; use core::char::{encode_utf16_raw, encode_utf8_raw}; +use core::clone::CloneToUninit; 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::ptr::addr_of_mut; 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}"; @@ -1050,3 +1048,13 @@ impl Hash for Wtf8 { 0xfeu8.hash(state) } } + +#[unstable(feature = "clone_to_uninit", issue = "126799")] +unsafe impl CloneToUninit for Wtf8 { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_to_uninit(&self, dst: *mut Self) { + // SAFETY: we're just a wrapper around [u8] + unsafe { self.bytes.clone_to_uninit(addr_of_mut!((*dst).bytes)) } + } +} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index c8ee365392f85..0fc63c5081b03 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::*; } //////////////////////////////////////////////////////////////////////////////// @@ -425,7 +412,6 @@ impl Builder { /// # Examples /// /// ``` - /// #![feature(thread_spawn_unchecked)] /// use std::thread; /// /// let builder = thread::Builder::new(); @@ -446,26 +432,25 @@ impl Builder { /// ``` /// /// [`io::Result`]: crate::io::Result - #[unstable(feature = "thread_spawn_unchecked", issue = "55132")] - pub unsafe fn spawn_unchecked<'a, F, T>(self, f: F) -> io::Result> + #[stable(feature = "thread_spawn_unchecked", since = "1.82.0")] + pub unsafe fn spawn_unchecked(self, f: F) -> io::Result> where F: FnOnce() -> T, - F: Send + 'a, - T: Send + 'a, + F: Send, + T: Send, { Ok(JoinHandle(unsafe { self.spawn_unchecked_(f, None) }?)) } - unsafe fn spawn_unchecked_<'a, 'scope, F, T>( + unsafe fn spawn_unchecked_<'scope, F, T>( self, f: F, scope_data: Option>, ) -> io::Result> where F: FnOnce() -> T, - F: Send + 'a, - T: Send + 'a, - 'scope: 'a, + F: Send, + T: Send, { let Builder { name, stack_size } = self; @@ -487,11 +472,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 +495,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 { @@ -550,7 +530,7 @@ impl Builder { // will call `decrement_num_running_threads` and therefore signal that this thread is // done. drop(their_packet); - // Here, the lifetime `'a` and even `'scope` can end. `main` keeps running for a bit + // Here, the lifetime `'scope` can end. `main` keeps running for a bit // after that before returning itself. }; @@ -699,17 +679,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"), }); } @@ -719,7 +704,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. @@ -836,7 +842,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. /// @@ -1107,7 +1113,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). @@ -1273,10 +1279,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 @@ -1316,25 +1364,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. // @@ -1457,15 +1500,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() } } 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 79c3c8f528efa..8becf713205ee 100644 --- a/library/std/tests/create_dir_all_bare.rs +++ b/library/std/tests/create_dir_all_bare.rs @@ -3,9 +3,8 @@ //! 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 6948670565662..dcd5cd7f6b9c7 100644 --- a/library/std/tests/run-time-detect.rs +++ b/library/std/tests/run-time-detect.rs @@ -4,6 +4,10 @@ all(target_arch = "arm", any(target_os = "linux", target_os = "android")), feature(stdarch_arm_feature_detection) )] +#![cfg_attr( + all(target_arch = "aarch64", any(target_os = "linux", target_os = "android")), + feature(stdarch_aarch64_feature_detection) +)] #![cfg_attr( all(target_arch = "powerpc", target_os = "linux"), feature(stdarch_powerpc_feature_detection) @@ -36,21 +40,34 @@ fn aarch64_linux() { println!("bf16: {}", is_aarch64_feature_detected!("bf16")); println!("bti: {}", is_aarch64_feature_detected!("bti")); println!("crc: {}", is_aarch64_feature_detected!("crc")); + println!("cssc: {}", is_aarch64_feature_detected!("cssc")); println!("dit: {}", is_aarch64_feature_detected!("dit")); println!("dotprod: {}", is_aarch64_feature_detected!("dotprod")); println!("dpb2: {}", is_aarch64_feature_detected!("dpb2")); println!("dpb: {}", is_aarch64_feature_detected!("dpb")); + println!("ecv: {}", is_aarch64_feature_detected!("ecv")); println!("f32mm: {}", is_aarch64_feature_detected!("f32mm")); println!("f64mm: {}", is_aarch64_feature_detected!("f64mm")); + println!("faminmax: {}", is_aarch64_feature_detected!("faminmax")); println!("fcma: {}", is_aarch64_feature_detected!("fcma")); println!("fhm: {}", is_aarch64_feature_detected!("fhm")); + println!("flagm2: {}", is_aarch64_feature_detected!("flagm2")); println!("flagm: {}", is_aarch64_feature_detected!("flagm")); println!("fp16: {}", is_aarch64_feature_detected!("fp16")); + println!("fp8: {}", is_aarch64_feature_detected!("fp8")); + println!("fp8dot2: {}", is_aarch64_feature_detected!("fp8dot2")); + println!("fp8dot4: {}", is_aarch64_feature_detected!("fp8dot4")); + println!("fp8fma: {}", is_aarch64_feature_detected!("fp8fma")); + println!("fpmr: {}", is_aarch64_feature_detected!("fpmr")); println!("frintts: {}", is_aarch64_feature_detected!("frintts")); + println!("hbc: {}", is_aarch64_feature_detected!("hbc")); println!("i8mm: {}", is_aarch64_feature_detected!("i8mm")); println!("jsconv: {}", is_aarch64_feature_detected!("jsconv")); + println!("lse128: {}", is_aarch64_feature_detected!("lse128")); println!("lse2: {}", is_aarch64_feature_detected!("lse2")); println!("lse: {}", is_aarch64_feature_detected!("lse")); + println!("lut: {}", is_aarch64_feature_detected!("lut")); + println!("mops: {}", is_aarch64_feature_detected!("mops")); println!("mte: {}", is_aarch64_feature_detected!("mte")); println!("neon: {}", is_aarch64_feature_detected!("neon")); println!("paca: {}", is_aarch64_feature_detected!("paca")); @@ -58,20 +75,37 @@ fn aarch64_linux() { println!("pmull: {}", is_aarch64_feature_detected!("pmull")); println!("rand: {}", is_aarch64_feature_detected!("rand")); println!("rcpc2: {}", is_aarch64_feature_detected!("rcpc2")); + println!("rcpc3: {}", is_aarch64_feature_detected!("rcpc3")); println!("rcpc: {}", is_aarch64_feature_detected!("rcpc")); println!("rdm: {}", is_aarch64_feature_detected!("rdm")); println!("sb: {}", is_aarch64_feature_detected!("sb")); println!("sha2: {}", is_aarch64_feature_detected!("sha2")); println!("sha3: {}", is_aarch64_feature_detected!("sha3")); println!("sm4: {}", is_aarch64_feature_detected!("sm4")); + println!("sme-f16f16: {}", is_aarch64_feature_detected!("sme-f16f16")); + println!("sme-f64f64: {}", is_aarch64_feature_detected!("sme-f64f64")); + println!("sme-f8f16: {}", is_aarch64_feature_detected!("sme-f8f16")); + println!("sme-f8f32: {}", is_aarch64_feature_detected!("sme-f8f32")); + println!("sme-fa64: {}", is_aarch64_feature_detected!("sme-fa64")); + println!("sme-i16i64: {}", is_aarch64_feature_detected!("sme-i16i64")); + println!("sme-lutv2: {}", is_aarch64_feature_detected!("sme-lutv2")); + println!("sme2: {}", is_aarch64_feature_detected!("sme2")); + println!("sme2p1: {}", is_aarch64_feature_detected!("sme2p1")); + println!("sme: {}", is_aarch64_feature_detected!("sme")); println!("ssbs: {}", is_aarch64_feature_detected!("ssbs")); + println!("ssve-fp8dot2: {}", is_aarch64_feature_detected!("ssve-fp8dot2")); + println!("ssve-fp8dot4: {}", is_aarch64_feature_detected!("ssve-fp8dot4")); + println!("ssve-fp8fma: {}", is_aarch64_feature_detected!("ssve-fp8fma")); + println!("sve-b16b16: {}", is_aarch64_feature_detected!("sve-b16b16")); println!("sve2-aes: {}", is_aarch64_feature_detected!("sve2-aes")); println!("sve2-bitperm: {}", is_aarch64_feature_detected!("sve2-bitperm")); println!("sve2-sha3: {}", is_aarch64_feature_detected!("sve2-sha3")); println!("sve2-sm4: {}", is_aarch64_feature_detected!("sve2-sm4")); println!("sve2: {}", is_aarch64_feature_detected!("sve2")); + println!("sve2p1: {}", is_aarch64_feature_detected!("sve2p1")); println!("sve: {}", is_aarch64_feature_detected!("sve")); println!("tme: {}", is_aarch64_feature_detected!("tme")); + println!("wfxt: {}", is_aarch64_feature_detected!("wfxt")); // tidy-alphabetical-end } 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 index 9f7596f1bc2c0..dab3182b81872 100644 --- a/library/std/tests/windows.rs +++ b/library/std/tests/windows.rs @@ -1,7 +1,9 @@ #![cfg(windows)] //! An external tests -use std::{ffi::OsString, os::windows::ffi::OsStringExt, path::PathBuf}; +use std::ffi::OsString; +use std::os::windows::ffi::OsStringExt; +use std::path::PathBuf; #[test] #[should_panic] diff --git a/library/stdarch b/library/stdarch index df3618d9f3516..d9466edb4c53c 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit df3618d9f35165f4bc548114e511c49c29e1fd9b +Subproject commit d9466edb4c53cece8686ee6e17b028436ddf4151 diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 169eeeca8c2e8..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"] diff --git a/library/test/src/bench.rs b/library/test/src/bench.rs index 9f34f54c3d60a..b71def3b03223 100644 --- a/library/test/src/bench.rs +++ b/library/test/src/bench.rs @@ -1,19 +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 b7d24405b775e..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 { 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 1854c6a76524d..b1545cbec438a 100644 --- a/library/test/src/helpers/concurrency.rs +++ b/library/test/src/helpers/concurrency.rs @@ -1,7 +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/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 71cb796b93705..632f8d161affa 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -24,6 +24,9 @@ #![feature(panic_can_unwind)] #![feature(test)] #![allow(internal_features)] +#![warn(rustdoc::unescaped_backticks)] + +pub use cli::TestOpts; pub use self::bench::{black_box, Bencher}; pub use self::console::run_tests_console; @@ -31,39 +34,31 @@ pub use self::options::{ColorConfig, Options, OutputFormat, RunIgnored, ShouldPa 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, PanicHookInfo}, - 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; @@ -82,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}; 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 c5b4ef01893c2..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; 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 65764c0ffc1b9..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 { 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..802cab989c6a9 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)] @@ -251,3 +250,37 @@ pub struct TestDescAndFn { pub desc: TestDesc, pub testfn: TestFn, } + +impl TestDescAndFn { + pub const fn new_doctest( + test_name: &'static str, + ignore: bool, + source_file: &'static str, + start_line: usize, + no_run: bool, + should_panic: bool, + testfn: TestFn, + ) -> Self { + Self { + desc: TestDesc { + name: StaticTestName(test_name), + ignore, + ignore_message: None, + source_file, + start_line, + start_col: 0, + end_line: 0, + end_col: 0, + compile_fail: false, + no_run, + should_panic: if should_panic { + options::ShouldPanic::Yes + } else { + options::ShouldPanic::No + }, + test_type: TestType::DocTest, + }, + testfn, + } + } +} diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index bbd1db8dfa57f..590de31a678ca 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -34,3 +34,10 @@ llvm-libunwind = [] # If crt-static is enabled, static link to `libunwind.a` provided by system # If crt-static is disabled, dynamic link to `libunwind.so` provided by system system-llvm-libunwind = [] + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + # #[cfg(bootstrap)] rtems + 'cfg(target_os, values("rtems"))', +] diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 45a1c334a44dd..26ed00bfbd53e 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)] -#![cfg_attr(bootstrap, feature(c_unwind))] #![feature(strict_provenance)] #![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] #![cfg_attr(not(target_env = "msvc"), feature(libc))] @@ -23,6 +22,7 @@ cfg_if::cfg_if! { target_os = "l4re", target_os = "none", target_os = "espidf", + target_os = "rtems", ))] { // These "unix" family members do not have unwinder. } else if #[cfg(any( @@ -166,8 +166,15 @@ extern "C" {} extern "C" {} #[cfg(target_os = "nto")] -#[link(name = "gcc_s")] -extern "C" {} +cfg_if::cfg_if! { + if #[cfg(target_env = "nto70")] { + #[link(name = "gcc")] + extern "C" {} + } else { + #[link(name = "gcc_s")] + extern "C" {} + } +} #[cfg(target_os = "hurd")] #[link(name = "gcc_s")] 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/library/windows_targets/Cargo.toml b/library/windows_targets/Cargo.toml new file mode 100644 index 0000000000000..94d7c8210647c --- /dev/null +++ b/library/windows_targets/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "windows-targets" +description = "A drop-in replacement for the real windows-targets crate for use in std only." +version = "0.0.0" +edition = "2021" + +[features] +# Enable using raw-dylib for Windows imports. +# This will eventually be the default. +windows_raw_dylib = [] diff --git a/library/std/src/sys/pal/windows/c/windows_targets.rs b/library/windows_targets/src/lib.rs similarity index 95% rename from library/std/src/sys/pal/windows/c/windows_targets.rs rename to library/windows_targets/src/lib.rs index 252bceb70942b..1965b6cf4ce8f 100644 --- a/library/std/src/sys/pal/windows/c/windows_targets.rs +++ b/library/windows_targets/src/lib.rs @@ -2,6 +2,10 @@ //! //! This is a simple wrapper around an `extern` block with a `#[link]` attribute. //! It's very roughly equivalent to the windows-targets crate. +#![no_std] +#![no_core] +#![feature(decl_macro)] +#![feature(no_core)] #[cfg(feature = "windows_raw_dylib")] pub macro link { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index dcae59a37ea19..9f6970c17ee5f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # standard library we currently track. [toolchain] -channel = "nightly-2024-07-16" +channel = "nightly-2024-09-08" 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

(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..dc03c4860e84b 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 @@ -725,7 +723,6 @@ fn test_reserve_exact() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_with_capacity() { let string = String::try_with_capacity(1000).unwrap(); assert_eq!(0, string.len()); @@ -736,7 +733,6 @@ fn test_try_with_capacity() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve() { // These are the interesting cases: // * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM) @@ -805,7 +801,6 @@ fn test_try_reserve() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve_exact() { // This is exactly the same as test_try_reserve with the method changed. // See that test for comments. diff --git a/library/alloc/tests/task.rs b/library/alloc/tests/task.rs index 034039a1eae9d..390dec14484ba 100644 --- a/library/alloc/tests/task.rs +++ b/library/alloc/tests/task.rs @@ -4,7 +4,7 @@ use alloc::task::{LocalWake, Wake}; use core::task::{LocalWaker, Waker}; #[test] -#[cfg_attr(miri, should_panic)] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it fails +#[cfg_attr(miri, ignore)] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it can fail fn test_waker_will_wake_clone() { struct NoopWaker; @@ -20,7 +20,7 @@ fn test_waker_will_wake_clone() { } #[test] -#[cfg_attr(miri, should_panic)] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it fails +#[cfg_attr(miri, ignore)] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it can fail fn test_local_waker_will_wake_clone() { struct NoopWaker; diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 71d79893e01d7..3722fb06a6a8a 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, @@ -1696,7 +1695,6 @@ fn test_reserve_exact() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_with_capacity() { let mut vec: Vec = Vec::try_with_capacity(5).unwrap(); assert_eq!(0, vec.len()); @@ -1708,7 +1706,6 @@ fn test_try_with_capacity() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve() { // These are the interesting cases: // * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM) @@ -1804,7 +1801,6 @@ fn test_try_reserve() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve_exact() { // This is exactly the same as test_try_reserve with the method changed. // See that test for comments. @@ -2572,7 +2568,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..f32ba8d5aa461 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(); @@ -1184,7 +1185,6 @@ fn test_reserve_exact_2() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_with_capacity() { let vec: VecDeque = VecDeque::try_with_capacity(5).unwrap(); assert_eq!(0, vec.len()); @@ -1195,7 +1195,6 @@ fn test_try_with_capacity() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve() { // These are the interesting cases: // * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM) @@ -1291,7 +1290,6 @@ fn test_try_reserve() { #[test] #[cfg_attr(miri, ignore)] // Miri does not support signalling OOM -#[cfg_attr(target_os = "android", ignore)] // Android used in CI has a broken dlmalloc fn test_try_reserve_exact() { // This is exactly the same as test_try_reserve with the method changed. // See that test for comments. diff --git a/library/alloc/tests/vec_deque_alloc_error.rs b/library/alloc/tests/vec_deque_alloc_error.rs index 8b516ddbc5c55..c41d8266eb457 100644 --- a/library/alloc/tests/vec_deque_alloc_error.rs +++ b/library/alloc/tests/vec_deque_alloc_error.rs @@ -1,11 +1,9 @@ #![feature(alloc_error_hook, allocator_api)] -use std::{ - alloc::{set_alloc_error_hook, AllocError, Allocator, Layout, System}, - collections::VecDeque, - panic::{catch_unwind, AssertUnwindSafe}, - ptr::NonNull, -}; +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")] diff --git a/library/backtrace b/library/backtrace index 72265bea21089..230570f2dac80 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit 72265bea210891ae47bbe6d4f17b493ef0606619 +Subproject commit 230570f2dac80a601f5c0b30da00cc9480bd35eb diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 65c3a57ae7116..9121227bc2615 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -46,6 +46,8 @@ check-cfg = [ 'cfg(bootstrap)', 'cfg(no_fp_fmt_parse)', 'cfg(stdarch_intel_sde)', + # #[cfg(bootstrap)] rtems + 'cfg(target_os, values("rtems"))', # core use #[path] imports to portable-simd `core_simd` crate # and to stdarch `core_arch` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg 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/lib.rs b/library/core/benches/lib.rs index 32d15c386cb1b..3f1c58bbd7204 100644 --- a/library/core/benches/lib.rs +++ b/library/core/benches/lib.rs @@ -8,6 +8,7 @@ #![feature(iter_array_chunks)] #![feature(iter_next_chunk)] #![feature(iter_advance_by)] +#![feature(isqrt)] extern crate test; 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/int_sqrt/mod.rs b/library/core/benches/num/int_sqrt/mod.rs new file mode 100644 index 0000000000000..3c9d173e456a1 --- /dev/null +++ b/library/core/benches/num/int_sqrt/mod.rs @@ -0,0 +1,62 @@ +use rand::Rng; +use test::{black_box, Bencher}; + +macro_rules! int_sqrt_bench { + ($t:ty, $predictable:ident, $random:ident, $random_small:ident, $random_uniform:ident) => { + #[bench] + fn $predictable(bench: &mut Bencher) { + bench.iter(|| { + for n in 0..(<$t>::BITS / 8) { + for i in 1..=(100 as $t) { + let x = black_box(i << (n * 8)); + black_box(x.isqrt()); + } + } + }); + } + + #[bench] + fn $random(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the whole range of the type. */ + let numbers: Vec<$t> = + (0..256).map(|_| rng.gen::<$t>() >> rng.gen_range(0..<$t>::BITS)).collect(); + bench.iter(|| { + for x in &numbers { + black_box(black_box(x).isqrt()); + } + }); + } + + #[bench] + fn $random_small(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the range 0..256. */ + let numbers: Vec<$t> = + (0..256).map(|_| (rng.gen::() >> rng.gen_range(0..u8::BITS)) as $t).collect(); + bench.iter(|| { + for x in &numbers { + black_box(black_box(x).isqrt()); + } + }); + } + + #[bench] + fn $random_uniform(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the whole range of the type. */ + let numbers: Vec<$t> = (0..256).map(|_| rng.gen::<$t>()).collect(); + bench.iter(|| { + for x in &numbers { + black_box(black_box(x).isqrt()); + } + }); + } + }; +} + +int_sqrt_bench! {u8, u8_sqrt_predictable, u8_sqrt_random, u8_sqrt_random_small, u8_sqrt_uniform} +int_sqrt_bench! {u16, u16_sqrt_predictable, u16_sqrt_random, u16_sqrt_random_small, u16_sqrt_uniform} +int_sqrt_bench! {u32, u32_sqrt_predictable, u32_sqrt_random, u32_sqrt_random_small, u32_sqrt_uniform} +int_sqrt_bench! {u64, u64_sqrt_predictable, u64_sqrt_random, u64_sqrt_random_small, u64_sqrt_uniform} +int_sqrt_bench! {u128, u128_sqrt_predictable, u128_sqrt_random, u128_sqrt_random_small, u128_sqrt_uniform} diff --git a/library/core/benches/num/mod.rs b/library/core/benches/num/mod.rs index 4922ee150d95f..7ff7443cfa7fe 100644 --- a/library/core/benches/num/mod.rs +++ b/library/core/benches/num/mod.rs @@ -2,8 +2,10 @@ mod dec2flt; mod flt2dec; mod int_log; mod int_pow; +mod int_sqrt; 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..789f9248e7fab 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::{assert_unsafe_precondition, 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,19 +63,27 @@ 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")] #[inline] #[rustc_allow_const_fn_unstable(ptr_alignment_type)] pub const fn from_size_align(size: usize, align: usize) -> Result { - if !align.is_power_of_two() { - return Err(LayoutError); + if Layout::is_size_align_valid(size, align) { + // SAFETY: Layout::is_size_align_valid checks the preconditions for this call. + unsafe { Ok(Layout { size, align: mem::transmute(align) }) } + } else { + Err(LayoutError) } + } - // SAFETY: just checked that align is a power of two. - Layout::from_size_alignment(size, unsafe { Alignment::new_unchecked(align) }) + const fn is_size_align_valid(size: usize, align: usize) -> bool { + let Some(align) = Alignment::new(align) else { return false }; + if size > Self::max_size_for_align(align) { + return false; + } + true } #[inline(always)] @@ -123,8 +129,17 @@ impl Layout { #[rustc_allow_const_fn_unstable(ptr_alignment_type)] #[requires(Layout::from_size_align(size, align).is_ok())] pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self { + assert_unsafe_precondition!( + check_library_ub, + "Layout::from_size_align_unchecked requires that align is a power of 2 \ + and the rounded-up allocation size does not exceed isize::MAX", + ( + size: usize = size, + align: usize = align, + ) => Layout::is_size_align_valid(size, align) + ); // SAFETY: the caller is required to uphold the preconditions. - unsafe { Layout { size, align: Alignment::new_unchecked(align) } } + unsafe { Layout { size, align: mem::transmute(align) } } } /// The minimum size in bytes for a memory block of this layout. @@ -188,6 +203,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 59f3b6841d531..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 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 3585bf07b597c..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. diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 8285c64ed2966..c63f261edabfa 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -23,7 +23,6 @@ mod equality; mod iter; pub(crate) use drain::drain_array_with; - #[stable(feature = "array_value_iter", since = "1.51.0")] pub use iter::IntoIter; @@ -38,7 +37,7 @@ pub use iter::IntoIter; /// /// # Example /// -/// Creating muliple copies of a `String`: +/// Creating multiple copies of a `String`: /// ```rust /// #![feature(array_repeat)] /// @@ -890,6 +889,7 @@ impl Guard<'_, T> { } impl Drop for Guard<'_, T> { + #[inline] fn drop(&mut self) { debug_assert!(self.initialized <= self.array_mut.len()); 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..29f4c041e166d 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -3,8 +3,12 @@ //! 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 crate::fmt::{self, Write}; +use safety::{ensures, requires}; use crate::mem::transmute; +use crate::{assert_unsafe_precondition, fmt}; + +#[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) } @@ -497,14 +504,18 @@ impl AsciiChar { /// Notably, it should not be expected to return hex digits, or any other /// reasonable extension of the decimal digits. /// - /// (This lose safety condition is intended to simplify soundness proofs + /// (This loose safety condition is intended to simplify soundness proofs /// when writing code using this method, since the implementation doesn't /// need something really specific, not to make those other arguments do /// something useful. It might be tightened before stabilization.) #[unstable(feature = "ascii_char", issue = "110998")] #[inline] pub const unsafe fn digit_unchecked(d: u8) -> Self { - debug_assert!(d < 10); + assert_unsafe_precondition!( + check_language_ub, + "`AsciiChar::digit_unchecked` input cannot exceed 9.", + (d: u8 = d) => d < 10 + ); // SAFETY: `'0'` through `'9'` are U+00030 through U+0039, // so because `d` must be 64 or less the addition can return at most @@ -583,9 +594,10 @@ impl fmt::Display for AsciiChar { #[unstable(feature = "ascii_char", issue = "110998")] impl fmt::Debug for AsciiChar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[inline] - fn backslash(a: AsciiChar) -> ([AsciiChar; 4], u8) { - ([AsciiChar::ReverseSolidus, a, AsciiChar::Null, AsciiChar::Null], 2) + use AsciiChar::{Apostrophe, Null, ReverseSolidus as Backslash}; + + fn backslash(a: AsciiChar) -> ([AsciiChar; 6], usize) { + ([Apostrophe, Backslash, a, Apostrophe, Null, Null], 4) } let (buf, len) = match self { @@ -595,24 +607,36 @@ impl fmt::Debug for AsciiChar { AsciiChar::LineFeed => backslash(AsciiChar::SmallN), AsciiChar::ReverseSolidus => backslash(AsciiChar::ReverseSolidus), AsciiChar::Apostrophe => backslash(AsciiChar::Apostrophe), - _ => { - let byte = self.to_u8(); - if !byte.is_ascii_control() { - ([*self, AsciiChar::Null, AsciiChar::Null, AsciiChar::Null], 1) - } else { - const HEX_DIGITS: [AsciiChar; 16] = *b"0123456789abcdef".as_ascii().unwrap(); + _ if self.to_u8().is_ascii_control() => { + const HEX_DIGITS: [AsciiChar; 16] = *b"0123456789abcdef".as_ascii().unwrap(); - let hi = HEX_DIGITS[usize::from(byte >> 4)]; - let lo = HEX_DIGITS[usize::from(byte & 0xf)]; - ([AsciiChar::ReverseSolidus, AsciiChar::SmallX, hi, lo], 4) - } + let byte = self.to_u8(); + let hi = HEX_DIGITS[usize::from(byte >> 4)]; + let lo = HEX_DIGITS[usize::from(byte & 0xf)]; + ([Apostrophe, Backslash, AsciiChar::SmallX, hi, lo, Apostrophe], 6) } + _ => ([Apostrophe, *self, Apostrophe, Null, Null, Null], 3), }; - f.write_char('\'')?; - for byte in &buf[..len as usize] { - f.write_str(byte.as_str())?; - } - f.write_char('\'') + f.write_str(buf[..len].as_str()) + } +} + +#[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/cell.rs b/library/core/src/cell.rs index b3189f14f9e47..a3a471a57c7aa 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -255,6 +255,7 @@ use crate::fmt::{self, Debug, Display}; use crate::marker::{PhantomData, Unsize}; use crate::mem; use crate::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; +use crate::pin::PinCoerceUnsized; use crate::ptr::{self, NonNull}; mod lazy; @@ -305,6 +306,7 @@ pub use once::OnceCell; /// See the [module-level documentation](self) for more. #[stable(feature = "rust1", since = "1.0.0")] #[repr(transparent)] +#[rustc_pub_transparent] pub struct Cell { value: UnsafeCell, } @@ -427,7 +429,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 /// @@ -1579,7 +1583,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 @@ -1747,7 +1751,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. @@ -1879,7 +1883,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". /// @@ -1936,7 +1940,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`. @@ -2052,6 +2056,7 @@ impl fmt::Display for RefMut<'_, T> { #[lang = "unsafe_cell"] #[stable(feature = "rust1", since = "1.0.0")] #[repr(transparent)] +#[rustc_pub_transparent] pub struct UnsafeCell { value: T, } @@ -2294,6 +2299,7 @@ impl UnsafeCell<*mut T> { /// See [`UnsafeCell`] for details. #[unstable(feature = "sync_unsafe_cell", issue = "95439")] #[repr(transparent)] +#[rustc_pub_transparent] pub struct SyncUnsafeCell { value: UnsafeCell, } @@ -2394,3 +2400,21 @@ fn assert_coerce_unsized( let _: Cell<&dyn Send> = c; let _: RefCell<&dyn Send> = d; } + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for UnsafeCell {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for SyncUnsafeCell {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Cell {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for RefCell {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<'b, T: ?Sized> PinCoerceUnsized for Ref<'b, T> {} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl<'b, T: ?Sized> PinCoerceUnsized for RefMut<'b, T> {} diff --git a/library/core/src/cell/lazy.rs b/library/core/src/cell/lazy.rs index 21452d40f9ded..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), diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index 872b4da4dbfda..87df8a4e272e8 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -1,6 +1,5 @@ use crate::cell::UnsafeCell; -use crate::fmt; -use crate::mem; +use crate::{fmt, mem}; /// A cell which can nominally be written to only once. /// @@ -310,7 +309,8 @@ impl OnceCell { /// ``` #[inline] #[stable(feature = "once_cell", since = "1.70.0")] - pub fn into_inner(self) -> Option { + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + pub const fn into_inner(self) -> Option { // Because `into_inner` takes `self` by value, the compiler statically verifies // that it is not currently borrowed. So it is safe to move out `Option`. self.inner.into_inner() 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 4186565c131ed..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,10 +225,7 @@ impl char { /// assert_eq!('❤', c); /// ``` #[stable(feature = "assoc_char_funcs", since = "1.52.0")] - #[rustc_const_stable( - feature = "const_char_from_u32_unchecked", - since = "CURRENT_RUSTC_VERSION" - )] + #[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 { @@ -1836,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 3c641a2e01c93..fa3c2075423bc 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -24,6 +24,8 @@ mod convert; 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")] @@ -31,20 +33,22 @@ pub use self::convert::ParseCharError; #[stable(feature = "decode_utf16", since = "1.9.0")] 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; // perma-unstable #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] 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; @@ -118,10 +122,10 @@ pub const fn from_u32(i: u32) -> Option { self::convert::from_u32(i) } -/// Converts a `u32` to a `char`, ignoring validity. Use [`char::from_u32_unchecked`]. +/// 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_stable(feature = "const_char_from_u32_unchecked", since = "CURRENT_RUSTC_VERSION")] +#[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 939b2be6dfaf1..c5f8bd7401e5e 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -36,8 +36,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::mem::{self, MaybeUninit}; -use crate::ptr; +mod uninit; /// A common trait for the ability to explicitly duplicate an object. /// @@ -160,6 +159,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. + #[lang = "clone_fn"] fn clone(&self) -> Self; /// Performs copy-assignment from `source`. @@ -245,7 +247,7 @@ pub unsafe trait CloneToUninit { /// * `dst` must be properly aligned. /// * `dst` must have the same [pointer metadata] (slice length or `dyn` vtable) as `self`. /// - /// [valid]: ptr#safety + /// [valid]: crate::ptr#safety /// [pointer metadata]: crate::ptr::metadata() /// /// # Panics @@ -269,124 +271,42 @@ pub unsafe trait CloneToUninit { #[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 { + #[inline] 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); - } + // SAFETY: we're calling a specialization with the same contract + unsafe { ::clone_one(self, dst) } } } #[unstable(feature = "clone_to_uninit", issue = "126799")] unsafe impl CloneToUninit for [T] { + #[inline] #[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); + unsafe fn clone_to_uninit(&self, dst: *mut Self) { + // SAFETY: we're calling a specialization with the same contract + unsafe { ::clone_slice(self, dst) } } } #[unstable(feature = "clone_to_uninit", issue = "126799")] -unsafe impl CloneToUninit for [T] { +unsafe impl CloneToUninit for str { + #[inline] #[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); - } + // SAFETY: str is just a [u8] with UTF-8 invariant + unsafe { self.as_bytes().clone_to_uninit(dst as *mut [u8]) } } } -/// 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); - } +#[unstable(feature = "clone_to_uninit", issue = "126799")] +unsafe impl CloneToUninit for crate::ffi::CStr { + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_to_uninit(&self, dst: *mut Self) { + // SAFETY: For now, CStr is just a #[repr(trasnsparent)] [c_char] with some invariants. + // And we can cast [c_char] to [u8] on all supported platforms (see: to_bytes_with_nul). + // The pointer metadata properly preserves the length (NUL included). + // See: `cstr_metadata_is_length_with_nul` in tests. + unsafe { self.to_bytes_with_nul().clone_to_uninit(dst as *mut [u8]) } } } diff --git a/library/core/src/clone/uninit.rs b/library/core/src/clone/uninit.rs new file mode 100644 index 0000000000000..8b738bec796de --- /dev/null +++ b/library/core/src/clone/uninit.rs @@ -0,0 +1,128 @@ +use crate::mem::{self, MaybeUninit}; +use crate::ptr; + +/// Private specialization trait used by CloneToUninit, as per +/// [the dev guide](https://std-dev-guide.rust-lang.org/policy/specialization.html). +pub(super) unsafe trait CopySpec: Clone { + unsafe fn clone_one(src: &Self, dst: *mut Self); + unsafe fn clone_slice(src: &[Self], dst: *mut [Self]); +} + +unsafe impl CopySpec for T { + #[inline] + default unsafe fn clone_one(src: &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, src.clone()); + } + } + + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + default unsafe fn clone_slice(src: &[Self], dst: *mut [Self]) { + let len = src.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 src { + // 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); + } +} + +// Specialized implementation for types that are [`Copy`], not just [`Clone`], +// and can therefore be copied bitwise. +unsafe impl CopySpec for T { + #[inline] + unsafe fn clone_one(src: &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(src, dst, 1); + } + } + + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_slice(src: &[Self], dst: *mut [Self]) { + let len = src.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(src.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); + } + } +} diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index cff75870790c5..a1ed993b7d9bf 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -246,15 +246,14 @@ use self::Ordering::*; )] #[rustc_diagnostic_item = "PartialEq"] 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")] @@ -1163,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 /// @@ -1180,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 /// @@ -1198,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 /// @@ -1215,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 /// 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..4c30290ff263b 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")] +#[rustc_trivial_field_reads] pub trait Default: Sized { /// Returns the "default value" for a type. /// diff --git a/library/core/src/error.rs b/library/core/src/error.rs index ca8983d4cbcfe..cac00b37d1fa7 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -1,8 +1,5 @@ #![doc = include_str!("error.md")] -#![stable(feature = "error_in_core", since = "CURRENT_RUSTC_VERSION")] - -#[cfg(test)] -mod tests; +#![stable(feature = "error_in_core", since = "1.81.0")] use crate::any::TypeId; use crate::fmt::{Debug, Display, Formatter, Result}; @@ -30,7 +27,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 +118,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. @@ -353,7 +350,7 @@ 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 /// @@ -376,7 +373,7 @@ 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 /// @@ -506,11 +503,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 +#[repr(transparent)] pub struct Request<'a>(Tagged + 'a>); impl<'a> Request<'a> { - /// Provide a value or other type with only static lifetimes. + /// Provides a value or other type with only static lifetimes. /// /// # Examples /// @@ -544,7 +541,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 /// @@ -578,7 +575,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 @@ -610,7 +607,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 @@ -652,7 +649,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>, @@ -663,7 +660,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>, @@ -674,13 +671,13 @@ 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 @@ -761,13 +758,14 @@ 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 diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 563f0a324e3f1..7808d42ab5de4 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,8 +89,9 @@ use crate::str; /// ``` /// /// [str]: prim@str "str" -#[derive(Hash)] +#[derive(PartialEq, Eq, Hash)] #[stable(feature = "core_c_str", since = "1.64.0")] +#[rustc_diagnostic_item = "cstr_type"] #[rustc_has_incoherent_inherent_impls] #[lang = "CStr"] // `fn from` in `impl From<&CStr> for Box` current implementation relies @@ -103,8 +99,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 @@ -278,7 +273,7 @@ impl CStr { #[inline] // inline is necessary for codegen to see strlen. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_cstr_from_ptr", since = "CURRENT_RUSTC_VERSION")] + #[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`. @@ -540,7 +535,7 @@ impl CStr { #[must_use] #[doc(alias("len", "strlen"))] #[stable(feature = "cstr_count_bytes", since = "1.79.0")] - #[rustc_const_stable(feature = "const_cstr_from_ptr", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0")] pub const fn count_bytes(&self) -> usize { self.inner.len() - 1 } @@ -678,15 +673,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] @@ -741,7 +730,7 @@ impl AsRef for CStr { /// located within `isize::MAX` from `ptr`. #[inline] #[unstable(feature = "cstr_internals", issue = "none")] -#[rustc_const_stable(feature = "const_cstr_from_ptr", since = "CURRENT_RUSTC_VERSION")] +#[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 { diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 88adc378477fd..dc107c5d22cdd 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -9,19 +9,16 @@ #![stable(feature = "core_ffi", since = "1.30.0")] #![allow(non_camel_case_types)] -use crate::fmt; - -#[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; @@ -113,7 +110,7 @@ mod c_char_definition { all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), all(target_os = "l4re", target_arch = "x86_64"), all( - any(target_os = "freebsd", target_os = "openbsd"), + any(target_os = "freebsd", target_os = "openbsd", target_os = "rtems"), any( target_arch = "aarch64", target_arch = "arm", @@ -191,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( diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 6a2e8b67d0c2a..3a224e4d8fe5f 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -3,7 +3,6 @@ //! Better known as "varargs". use crate::ffi::c_void; - #[allow(unused_imports)] use crate::fmt; use crate::marker::PhantomData; @@ -23,7 +22,7 @@ use crate::ops::{Deref, DerefMut}; target_os = "uefi", windows, ))] -#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 +#[repr(transparent)] #[lang = "va_list"] pub struct VaListImpl<'f> { ptr: *mut c_void, @@ -115,7 +114,7 @@ pub struct VaListImpl<'f> { } /// A wrapper for a `va_list` -#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 +#[repr(transparent)] #[derive(Debug)] pub struct VaList<'a, 'f: 'a> { #[cfg(any( @@ -162,7 +161,7 @@ pub struct VaList<'a, 'f: 'a> { windows, ))] impl<'f> VaListImpl<'f> { - /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. + /// 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 } @@ -182,7 +181,7 @@ impl<'f> VaListImpl<'f> { not(windows), ))] impl<'f> VaListImpl<'f> { - /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. + /// 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 } diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 4ccb585862cdf..c7c462a4df1f5 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -78,7 +78,7 @@ impl fmt::Write for PadAdapter<'_, '_> { /// /// assert_eq!( /// format!("{:?}", Foo { bar: 10, baz: "Hello World".to_string() }), -/// "Foo { bar: 10, baz: \"Hello World\" }", +/// r#"Foo { bar: 10, baz: "Hello World" }"#, /// ); /// ``` #[must_use = "must eventually call `finish()` on Debug builders"] @@ -125,7 +125,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// /// assert_eq!( /// format!("{:?}", Bar { bar: 10, another: "Hello World".to_string() }), - /// "Bar { bar: 10, another: \"Hello World\", nonexistent_field: 1 }", + /// r#"Bar { bar: 10, another: "Hello World", nonexistent_field: 1 }"#, /// ); /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] @@ -237,7 +237,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// /// assert_eq!( /// format!("{:?}", Bar { bar: 10, baz: "Hello World".to_string() }), - /// "Bar { bar: 10, baz: \"Hello World\" }", + /// r#"Bar { bar: 10, baz: "Hello World" }"#, /// ); /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] @@ -280,7 +280,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// /// assert_eq!( /// format!("{:?}", Foo(10, "Hello World".to_string())), -/// "Foo(10, \"Hello World\")", +/// r#"Foo(10, "Hello World")"#, /// ); /// ``` #[must_use = "must eventually call `finish()` on Debug builders"] @@ -322,7 +322,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { /// /// assert_eq!( /// format!("{:?}", Foo(10, "Hello World".to_string())), - /// "Foo(10, \"Hello World\")", + /// r#"Foo(10, "Hello World")"#, /// ); /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] @@ -360,6 +360,51 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { self } + /// Marks the tuple struct as non-exhaustive, indicating to the reader that there are some + /// other fields that are not shown in the debug representation. + /// + /// # Examples + /// + /// ``` + /// #![feature(debug_more_non_exhaustive)] + /// + /// use std::fmt; + /// + /// struct Foo(i32, String); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + /// fmt.debug_tuple("Foo") + /// .field(&self.0) + /// .finish_non_exhaustive() // Show that some other field(s) exist. + /// } + /// } + /// + /// assert_eq!( + /// format!("{:?}", Foo(10, "secret!".to_owned())), + /// "Foo(10, ..)", + /// ); + /// ``` + #[unstable(feature = "debug_more_non_exhaustive", issue = "127942")] + pub fn finish_non_exhaustive(&mut self) -> fmt::Result { + self.result = self.result.and_then(|_| { + if self.fields > 0 { + if self.is_pretty() { + let mut slot = None; + let mut state = Default::default(); + let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state); + writer.write_str("..\n")?; + self.fmt.write_str(")") + } else { + self.fmt.write_str(", ..)") + } + } else { + self.fmt.write_str("(..)") + } + }); + self.result + } + /// Finishes output and returns any error encountered. /// /// # Examples @@ -381,7 +426,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { /// /// assert_eq!( /// format!("{:?}", Foo(10, "Hello World".to_string())), - /// "Foo(10, \"Hello World\")", + /// r#"Foo(10, "Hello World")"#, /// ); /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] @@ -402,6 +447,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, @@ -554,6 +600,56 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { self } + /// Marks the set as non-exhaustive, indicating to the reader that there are some other + /// elements that are not shown in the debug representation. + /// + /// # Examples + /// + /// ``` + /// #![feature(debug_more_non_exhaustive)] + /// + /// use std::fmt; + /// + /// struct Foo(Vec); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + /// // Print at most two elements, abbreviate the rest + /// let mut f = fmt.debug_set(); + /// let mut f = f.entries(self.0.iter().take(2)); + /// if self.0.len() > 2 { + /// f.finish_non_exhaustive() + /// } else { + /// f.finish() + /// } + /// } + /// } + /// + /// assert_eq!( + /// format!("{:?}", Foo(vec![1, 2, 3, 4])), + /// "{1, 2, ..}", + /// ); + /// ``` + #[unstable(feature = "debug_more_non_exhaustive", issue = "127942")] + pub fn finish_non_exhaustive(&mut self) -> fmt::Result { + self.inner.result = self.inner.result.and_then(|_| { + if self.inner.has_fields { + if self.inner.is_pretty() { + let mut slot = None; + let mut state = Default::default(); + let mut writer = PadAdapter::wrap(self.inner.fmt, &mut slot, &mut state); + writer.write_str("..\n")?; + self.inner.fmt.write_str("}") + } else { + self.inner.fmt.write_str(", ..}") + } + } else { + self.inner.fmt.write_str("..}") + } + }); + self.inner.result + } + /// Finishes output and returns any error encountered. /// /// # Examples @@ -578,7 +674,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 } } @@ -697,6 +794,55 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { self } + /// Marks the list as non-exhaustive, indicating to the reader that there are some other + /// elements that are not shown in the debug representation. + /// + /// # Examples + /// + /// ``` + /// #![feature(debug_more_non_exhaustive)] + /// + /// use std::fmt; + /// + /// struct Foo(Vec); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + /// // Print at most two elements, abbreviate the rest + /// let mut f = fmt.debug_list(); + /// let mut f = f.entries(self.0.iter().take(2)); + /// if self.0.len() > 2 { + /// f.finish_non_exhaustive() + /// } else { + /// f.finish() + /// } + /// } + /// } + /// + /// assert_eq!( + /// format!("{:?}", Foo(vec![1, 2, 3, 4])), + /// "[1, 2, ..]", + /// ); + /// ``` + #[unstable(feature = "debug_more_non_exhaustive", issue = "127942")] + pub fn finish_non_exhaustive(&mut self) -> fmt::Result { + self.inner.result.and_then(|_| { + if self.inner.has_fields { + if self.inner.is_pretty() { + let mut slot = None; + let mut state = Default::default(); + let mut writer = PadAdapter::wrap(self.inner.fmt, &mut slot, &mut state); + writer.write_str("..\n")?; + self.inner.fmt.write_str("]") + } else { + self.inner.fmt.write_str(", ..]") + } + } else { + self.inner.fmt.write_str("..]") + } + }) + } + /// Finishes output and returns any error encountered. /// /// # Examples @@ -721,7 +867,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 } } @@ -747,7 +894,7 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// /// assert_eq!( /// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])), -/// "{\"A\": 10, \"B\": 11}", +/// r#"{"A": 10, "B": 11}"#, /// ); /// ``` #[must_use = "must eventually call `finish()` on Debug builders"] @@ -787,7 +934,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// /// assert_eq!( /// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])), - /// "{\"whole\": [(\"A\", 10), (\"B\", 11)]}", + /// r#"{"whole": [("A", 10), ("B", 11)]}"#, /// ); /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] @@ -823,7 +970,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// /// assert_eq!( /// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])), - /// "{\"whole\": [(\"A\", 10), (\"B\", 11)]}", + /// r#"{"whole": [("A", 10), ("B", 11)]}"#, /// ); /// ``` #[stable(feature = "debug_map_key_value", since = "1.42.0")] @@ -899,7 +1046,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// /// assert_eq!( /// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])), - /// "{\"whole\": [(\"A\", 10), (\"B\", 11)]}", + /// r#"{"whole": [("A", 10), ("B", 11)]}"#, /// ); /// ``` #[stable(feature = "debug_map_key_value", since = "1.42.0")] @@ -957,7 +1104,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// /// assert_eq!( /// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])), - /// "{\"A\": 10, \"B\": 11}", + /// r#"{"A": 10, "B": 11}"#, /// ); /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] @@ -973,6 +1120,62 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { self } + /// Marks the map as non-exhaustive, indicating to the reader that there are some other + /// entries that are not shown in the debug representation. + /// + /// # Examples + /// + /// ``` + /// #![feature(debug_more_non_exhaustive)] + /// + /// use std::fmt; + /// + /// struct Foo(Vec<(String, i32)>); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + /// // Print at most two elements, abbreviate the rest + /// let mut f = fmt.debug_map(); + /// let mut f = f.entries(self.0.iter().take(2).map(|&(ref k, ref v)| (k, v))); + /// if self.0.len() > 2 { + /// f.finish_non_exhaustive() + /// } else { + /// f.finish() + /// } + /// } + /// } + /// + /// assert_eq!( + /// format!("{:?}", Foo(vec![ + /// ("A".to_string(), 10), + /// ("B".to_string(), 11), + /// ("C".to_string(), 12), + /// ])), + /// r#"{"A": 10, "B": 11, ..}"#, + /// ); + /// ``` + #[unstable(feature = "debug_more_non_exhaustive", issue = "127942")] + pub fn finish_non_exhaustive(&mut self) -> fmt::Result { + self.result = self.result.and_then(|_| { + assert!(!self.has_key, "attempted to finish a map with a partial entry"); + + if self.has_fields { + if self.is_pretty() { + let mut slot = None; + let mut state = Default::default(); + let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state); + writer.write_str("..\n")?; + self.fmt.write_str("}") + } else { + self.fmt.write_str(", ..}") + } + } else { + self.fmt.write_str("..}") + } + }); + self.result + } + /// Finishes output and returns any error encountered. /// /// # Panics @@ -997,16 +1200,17 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// /// assert_eq!( /// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])), - /// "{\"A\": 10, \"B\": 11}", + /// r#"{"A": 10, "B": 11}"#, /// ); /// ``` #[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 { @@ -1014,7 +1218,8 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { } } -/// Implements [`fmt::Debug`] and [`fmt::Display`] using a function. +/// Creates a type whose [`fmt::Debug`] and [`fmt::Display`] impls are provided with the function +/// `f`. /// /// # Examples /// @@ -1026,17 +1231,25 @@ 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::from_fn(|f| write!(f, "{value:?}")); /// assert_eq!(format!("{}", wrapped), "'a'"); /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] -pub struct FormatterFn(pub F) +pub fn from_fn) -> fmt::Result>(f: F) -> FromFn { + FromFn(f) +} + +/// Implements [`fmt::Debug`] and [`fmt::Display`] using a function. +/// +/// Created with [`from_fn`]. +#[unstable(feature = "debug_closure_helpers", issue = "117729")] +pub struct FromFn(F) where F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result; #[unstable(feature = "debug_closure_helpers", issue = "117729")] -impl fmt::Debug for FormatterFn +impl fmt::Debug for FromFn where F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result, { @@ -1046,7 +1259,7 @@ where } #[unstable(feature = "debug_closure_helpers", issue = "117729")] -impl fmt::Display for FormatterFn +impl fmt::Display for FromFn where F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result, { diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 80c45fce2f0a0..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 { diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 25ab5b2db9641..45c2b6a6a0f73 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,12 +33,11 @@ pub enum Alignment { Center, } +#[unstable(feature = "debug_closure_helpers", issue = "117729")] +pub use self::builders::{from_fn, FromFn}; #[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; - /// The type returned by formatter methods. /// /// # Examples @@ -354,7 +350,7 @@ impl<'a> Arguments<'a> { 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: @@ -396,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. /// @@ -1129,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. @@ -1257,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)?; @@ -1398,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 @@ -1474,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, @@ -1503,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. /// @@ -1627,6 +1626,11 @@ impl<'a> Formatter<'a> { self.buf.write_str(data) } + /// Glue for usage of the [`write!`] macro with implementors of this trait. + /// + /// This method should generally not be invoked manually, but rather through + /// the [`write!`] macro itself. + /// /// Writes some formatted information into this instance. /// /// # Examples @@ -1655,7 +1659,7 @@ impl<'a> Formatter<'a> { } } - /// Flags for formatting + /// Returns flags for formatting. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[deprecated( @@ -1667,7 +1671,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 /// @@ -1700,7 +1704,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 /// @@ -1740,7 +1744,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 /// @@ -1770,8 +1774,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 /// @@ -1967,8 +1971,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>( @@ -1982,8 +1987,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>( @@ -2000,8 +2006,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>( @@ -2021,8 +2028,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>( @@ -2045,8 +2053,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>( @@ -2072,7 +2081,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")] @@ -2121,8 +2130,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 { @@ -2131,8 +2141,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>( @@ -2147,8 +2158,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>( @@ -2165,8 +2177,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>( @@ -2185,8 +2198,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>( @@ -2207,8 +2221,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>( @@ -2496,8 +2510,9 @@ impl Pointer for *const T { } } -/// 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 3a5a5af8bf5d3..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: diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs index 65a4d537cc74d..eee4a9e4c6c89 100644 --- a/library/core/src/fmt/rt.rs +++ b/library/core/src/fmt/rt.rs @@ -110,39 +110,43 @@ impl<'a> Argument<'a> { } #[inline(always)] - pub fn new_display<'b, T: Display>(x: &'b T) -> Argument<'_> { + pub fn new_display<'b, T: Display>(x: &'b T) -> Argument<'b> { Self::new(x, Display::fmt) } #[inline(always)] - pub fn new_debug<'b, T: Debug>(x: &'b T) -> Argument<'_> { + pub fn new_debug<'b, T: Debug>(x: &'b T) -> Argument<'b> { Self::new(x, Debug::fmt) } #[inline(always)] - pub fn new_octal<'b, T: Octal>(x: &'b T) -> Argument<'_> { + pub fn new_debug_noop<'b, T: Debug>(x: &'b T) -> Argument<'b> { + Self::new(x, |_, _| Ok(())) + } + #[inline(always)] + pub fn new_octal<'b, T: Octal>(x: &'b T) -> Argument<'b> { Self::new(x, Octal::fmt) } #[inline(always)] - pub fn new_lower_hex<'b, T: LowerHex>(x: &'b T) -> Argument<'_> { + pub fn new_lower_hex<'b, T: LowerHex>(x: &'b T) -> Argument<'b> { Self::new(x, LowerHex::fmt) } #[inline(always)] - pub fn new_upper_hex<'b, T: UpperHex>(x: &'b T) -> Argument<'_> { + pub fn new_upper_hex<'b, T: UpperHex>(x: &'b T) -> Argument<'b> { Self::new(x, UpperHex::fmt) } #[inline(always)] - pub fn new_pointer<'b, T: Pointer>(x: &'b T) -> Argument<'_> { + pub fn new_pointer<'b, T: Pointer>(x: &'b T) -> Argument<'b> { Self::new(x, Pointer::fmt) } #[inline(always)] - pub fn new_binary<'b, T: Binary>(x: &'b T) -> Argument<'_> { + pub fn new_binary<'b, T: Binary>(x: &'b T) -> Argument<'b> { Self::new(x, Binary::fmt) } #[inline(always)] - pub fn new_lower_exp<'b, T: LowerExp>(x: &'b T) -> Argument<'_> { + pub fn new_lower_exp<'b, T: LowerExp>(x: &'b T) -> Argument<'b> { Self::new(x, LowerExp::fmt) } #[inline(always)] - pub fn new_upper_exp<'b, T: UpperExp>(x: &'b T) -> Argument<'_> { + pub fn new_upper_exp<'b, T: UpperExp>(x: &'b T) -> Argument<'b> { Self::new(x, UpperExp::fmt) } #[inline(always)] diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs index 63193bbfb35e8..16ac77fa15045 100644 --- a/library/core/src/future/async_drop.rs +++ b/library/core/src/future/async_drop.rs @@ -157,7 +157,7 @@ async unsafe fn surface_drop_in_place(ptr: *mut T) { unsafe { crate::ops::fallback_surface_drop(&mut *ptr) } } -/// Wraps a future to continue outputing `Poll::Ready(())` once after +/// Wraps a future to continue outputting `Poll::Ready(())` once after /// wrapped future completes by returning `Poll::Ready(())` on poll. This /// is useful for constructing async destructors to guarantee this /// "fuse" property @@ -205,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) @@ -223,7 +223,7 @@ where /// # Safety /// /// Same as `async_drop_in_place` except is lazy to avoid creating -/// multiple mutable refernces. +/// multiple mutable references. #[lang = "async_drop_defer"] async unsafe fn defer(to_drop: *mut T) { // SAFETY: same safety requirements as `async_drop_in_place` @@ -235,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"] diff --git a/library/core/src/future/future.rs b/library/core/src/future/future.rs index c80cfdcebf70d..ca1c2d1ca1f2e 100644 --- a/library/core/src/future/future.rs +++ b/library/core/src/future/future.rs @@ -38,7 +38,7 @@ pub trait Future { #[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 3a1451abfa40b..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 = "126482")] -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/future/ready.rs b/library/core/src/future/ready.rs index a07b63fb62b90..b562ad4d8860d 100644 --- a/library/core/src/future/ready.rs +++ b/library/core/src/future/ready.rs @@ -34,13 +34,12 @@ impl Ready { /// # Examples /// /// ``` - /// #![feature(ready_into_inner)] /// use std::future; /// /// let a = future::ready(1); /// assert_eq!(a.into_inner(), 1); /// ``` - #[unstable(feature = "ready_into_inner", issue = "101196")] + #[stable(feature = "ready_into_inner", since = "1.82.0")] #[must_use] #[inline] pub fn into_inner(self) -> T { diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index da734466263ab..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; @@ -806,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 0d1ac64aa56cf..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. /// diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index b3e36e6fbc4ac..a69f0afdb0a59 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -1,10 +1,10 @@ #![stable(feature = "core_hint", since = "1.27.0")] //! 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. @@ -195,8 +195,8 @@ pub const unsafe fn unreachable_unchecked() -> ! { #[track_caller] #[inline(always)] #[doc(alias = "assume")] -#[stable(feature = "hint_assert_unchecked", since = "CURRENT_RUSTC_VERSION")] -#[rustc_const_stable(feature = "hint_assert_unchecked", since = "CURRENT_RUSTC_VERSION")] +#[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 { diff --git a/library/core/src/internal_macros.rs b/library/core/src/internal_macros.rs index bf53b2245ac59..fe4fa80263c28 100644 --- a/library/core/src/internal_macros.rs +++ b/library/core/src/internal_macros.rs @@ -80,7 +80,7 @@ macro_rules! forward_ref_op_assign { } } -/// Create a zero-size type similar to a closure type, but named. +/// Creates a zero-size type similar to a closure type, but named. macro_rules! impl_fn_for_zst { ($( $( #[$attr: meta] )* diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 4589d99d9b491..d6e54edc1cfcb 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -951,7 +951,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. @@ -1015,6 +1014,27 @@ 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. +#[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 } +} + extern "rust-intrinsic" { /// Executes a breakpoint trap, for inspection by a debugger. /// @@ -1022,46 +1042,9 @@ 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. + /// Executes a breakpoint trap, for inspection by a debugger. /// /// 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] // FIXME: Kani currently does not support annotating intrinsics. // See https://github.com/model-checking/kani/issues/3325 @@ -1305,7 +1288,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; @@ -1317,7 +1300,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; @@ -1330,7 +1313,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. @@ -1416,7 +1399,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(); @@ -1594,6 +1577,12 @@ extern "rust-intrinsic" { #[rustc_diagnostic_item = "intrinsics_unaligned_volatile_store"] pub fn unaligned_volatile_store(dst: *mut T, val: T); + /// Returns the square root of an `f16` + /// + /// The stabilized version of this intrinsic is + /// [`f16::sqrt`](../../std/primitive.f16.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf16(x: f16) -> f16; /// Returns the square root of an `f32` /// /// The stabilized version of this intrinsic is @@ -1606,6 +1595,12 @@ extern "rust-intrinsic" { /// [`f64::sqrt`](../../std/primitive.f64.html#method.sqrt) #[rustc_nounwind] pub fn sqrtf64(x: f64) -> f64; + /// Returns the square root of an `f128` + /// + /// The stabilized version of this intrinsic is + /// [`f128::sqrt`](../../std/primitive.f128.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf128(x: f128) -> f128; /// Raises an `f16` to an integer power. /// @@ -1632,6 +1627,12 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn powif128(a: f128, x: i32) -> f128; + /// Returns the sine of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::sin`](../../std/primitive.f16.html#method.sin) + #[rustc_nounwind] + pub fn sinf16(x: f16) -> f16; /// Returns the sine of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1644,7 +1645,19 @@ extern "rust-intrinsic" { /// [`f64::sin`](../../std/primitive.f64.html#method.sin) #[rustc_nounwind] pub fn sinf64(x: f64) -> f64; + /// Returns the sine of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::sin`](../../std/primitive.f128.html#method.sin) + #[rustc_nounwind] + pub fn sinf128(x: f128) -> f128; + /// Returns the cosine of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::cos`](../../std/primitive.f16.html#method.cos) + #[rustc_nounwind] + pub fn cosf16(x: f16) -> f16; /// Returns the cosine of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1657,7 +1670,19 @@ extern "rust-intrinsic" { /// [`f64::cos`](../../std/primitive.f64.html#method.cos) #[rustc_nounwind] pub fn cosf64(x: f64) -> f64; + /// Returns the cosine of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::cos`](../../std/primitive.f128.html#method.cos) + #[rustc_nounwind] + pub fn cosf128(x: f128) -> f128; + /// Raises an `f16` to an `f16` power. + /// + /// The stabilized version of this intrinsic is + /// [`f16::powf`](../../std/primitive.f16.html#method.powf) + #[rustc_nounwind] + pub fn powf16(a: f16, x: f16) -> f16; /// Raises an `f32` to an `f32` power. /// /// The stabilized version of this intrinsic is @@ -1670,7 +1695,19 @@ extern "rust-intrinsic" { /// [`f64::powf`](../../std/primitive.f64.html#method.powf) #[rustc_nounwind] pub fn powf64(a: f64, x: f64) -> f64; + /// Raises an `f128` to an `f128` power. + /// + /// The stabilized version of this intrinsic is + /// [`f128::powf`](../../std/primitive.f128.html#method.powf) + #[rustc_nounwind] + pub fn powf128(a: f128, x: f128) -> f128; + /// Returns the exponential of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::exp`](../../std/primitive.f16.html#method.exp) + #[rustc_nounwind] + pub fn expf16(x: f16) -> f16; /// Returns the exponential of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1683,7 +1720,19 @@ extern "rust-intrinsic" { /// [`f64::exp`](../../std/primitive.f64.html#method.exp) #[rustc_nounwind] pub fn expf64(x: f64) -> f64; + /// Returns the exponential of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::exp`](../../std/primitive.f128.html#method.exp) + #[rustc_nounwind] + pub fn expf128(x: f128) -> f128; + /// Returns 2 raised to the power of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::exp2`](../../std/primitive.f16.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f16(x: f16) -> f16; /// Returns 2 raised to the power of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1696,7 +1745,19 @@ extern "rust-intrinsic" { /// [`f64::exp2`](../../std/primitive.f64.html#method.exp2) #[rustc_nounwind] pub fn exp2f64(x: f64) -> f64; + /// Returns 2 raised to the power of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::exp2`](../../std/primitive.f128.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f128(x: f128) -> f128; + /// Returns the natural logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::ln`](../../std/primitive.f16.html#method.ln) + #[rustc_nounwind] + pub fn logf16(x: f16) -> f16; /// Returns the natural logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1709,7 +1770,19 @@ extern "rust-intrinsic" { /// [`f64::ln`](../../std/primitive.f64.html#method.ln) #[rustc_nounwind] pub fn logf64(x: f64) -> f64; + /// Returns the natural logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::ln`](../../std/primitive.f128.html#method.ln) + #[rustc_nounwind] + pub fn logf128(x: f128) -> f128; + /// Returns the base 10 logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::log10`](../../std/primitive.f16.html#method.log10) + #[rustc_nounwind] + pub fn log10f16(x: f16) -> f16; /// Returns the base 10 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1722,7 +1795,19 @@ extern "rust-intrinsic" { /// [`f64::log10`](../../std/primitive.f64.html#method.log10) #[rustc_nounwind] pub fn log10f64(x: f64) -> f64; + /// Returns the base 10 logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::log10`](../../std/primitive.f128.html#method.log10) + #[rustc_nounwind] + pub fn log10f128(x: f128) -> f128; + /// Returns the base 2 logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::log2`](../../std/primitive.f16.html#method.log2) + #[rustc_nounwind] + pub fn log2f16(x: f16) -> f16; /// Returns the base 2 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1735,7 +1820,19 @@ extern "rust-intrinsic" { /// [`f64::log2`](../../std/primitive.f64.html#method.log2) #[rustc_nounwind] pub fn log2f64(x: f64) -> f64; + /// Returns the base 2 logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::log2`](../../std/primitive.f128.html#method.log2) + #[rustc_nounwind] + pub fn log2f128(x: f128) -> f128; + /// Returns `a * b + c` for `f16` values. + /// + /// The stabilized version of this intrinsic is + /// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf16(a: f16, b: f16, c: f16) -> f16; /// Returns `a * b + c` for `f32` values. /// /// The stabilized version of this intrinsic is @@ -1748,7 +1845,19 @@ extern "rust-intrinsic" { /// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) #[rustc_nounwind] pub fn fmaf64(a: f64, b: f64, c: f64) -> f64; + /// Returns `a * b + c` for `f128` values. + /// + /// The stabilized version of this intrinsic is + /// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf128(a: f128, b: f128, c: f128) -> f128; + /// Returns the absolute value of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::abs`](../../std/primitive.f16.html#method.abs) + #[rustc_nounwind] + pub fn fabsf16(x: f16) -> f16; /// Returns the absolute value of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1761,7 +1870,25 @@ extern "rust-intrinsic" { /// [`f64::abs`](../../std/primitive.f64.html#method.abs) #[rustc_nounwind] pub fn fabsf64(x: f64) -> f64; + /// Returns the absolute value of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::abs`](../../std/primitive.f128.html#method.abs) + #[rustc_nounwind] + pub fn fabsf128(x: f128) -> f128; + /// Returns the minimum of two `f16` values. + /// + /// 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 + /// [`f16::min`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn minnumf16(x: f16, y: f16) -> f16; /// Returns the minimum of two `f32` values. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -1786,6 +1913,31 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn minnumf64(x: f64, y: f64) -> f64; + /// Returns the minimum of two `f128` values. + /// + /// 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 + /// [`f128::min`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn minnumf128(x: f128, y: f128) -> f128; + + /// Returns the maximum of two `f16` values. + /// + /// 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 + /// [`f16::max`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn maxnumf16(x: f16, y: f16) -> f16; /// Returns the maximum of two `f32` values. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -1810,7 +1962,25 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn maxnumf64(x: f64, y: f64) -> f64; + /// Returns the maximum of two `f128` values. + /// + /// 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 + /// [`f128::max`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn maxnumf128(x: f128, y: f128) -> f128; + /// Copies the sign from `y` to `x` for `f16` values. + /// + /// The stabilized version of this intrinsic is + /// [`f16::copysign`](../../std/primitive.f16.html#method.copysign) + #[rustc_nounwind] + pub fn copysignf16(x: f16, y: f16) -> f16; /// Copies the sign from `y` to `x` for `f32` values. /// /// The stabilized version of this intrinsic is @@ -1823,7 +1993,19 @@ extern "rust-intrinsic" { /// [`f64::copysign`](../../std/primitive.f64.html#method.copysign) #[rustc_nounwind] pub fn copysignf64(x: f64, y: f64) -> f64; + /// Copies the sign from `y` to `x` for `f128` values. + /// + /// The stabilized version of this intrinsic is + /// [`f128::copysign`](../../std/primitive.f128.html#method.copysign) + #[rustc_nounwind] + pub fn copysignf128(x: f128, y: f128) -> f128; + /// Returns the largest integer less than or equal to an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::floor`](../../std/primitive.f16.html#method.floor) + #[rustc_nounwind] + pub fn floorf16(x: f16) -> f16; /// Returns the largest integer less than or equal to an `f32`. /// /// The stabilized version of this intrinsic is @@ -1836,7 +2018,19 @@ extern "rust-intrinsic" { /// [`f64::floor`](../../std/primitive.f64.html#method.floor) #[rustc_nounwind] pub fn floorf64(x: f64) -> f64; + /// Returns the largest integer less than or equal to an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::floor`](../../std/primitive.f128.html#method.floor) + #[rustc_nounwind] + pub fn floorf128(x: f128) -> f128; + /// Returns the smallest integer greater than or equal to an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::ceil`](../../std/primitive.f16.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf16(x: f16) -> f16; /// Returns the smallest integer greater than or equal to an `f32`. /// /// The stabilized version of this intrinsic is @@ -1849,7 +2043,19 @@ extern "rust-intrinsic" { /// [`f64::ceil`](../../std/primitive.f64.html#method.ceil) #[rustc_nounwind] pub fn ceilf64(x: f64) -> f64; + /// Returns the smallest integer greater than or equal to an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::ceil`](../../std/primitive.f128.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf128(x: f128) -> f128; + /// Returns the integer part of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::trunc`](../../std/primitive.f16.html#method.trunc) + #[rustc_nounwind] + pub fn truncf16(x: f16) -> f16; /// Returns the integer part of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1862,7 +2068,25 @@ extern "rust-intrinsic" { /// [`f64::trunc`](../../std/primitive.f64.html#method.trunc) #[rustc_nounwind] pub fn truncf64(x: f64) -> f64; + /// Returns the integer part of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::trunc`](../../std/primitive.f128.html#method.trunc) + #[rustc_nounwind] + pub fn truncf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf16` and `roundevenf16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::round_ties_even`](../../std/primitive.f16.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, /// so this rounds half-way cases to the number with an even least significant digit. /// @@ -1887,7 +2111,25 @@ extern "rust-intrinsic" { /// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) #[rustc_nounwind] pub fn rintf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf128` and `roundevenf128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::round_ties_even`](../../std/primitive.f128.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, /// so this rounds half-way cases to the number with an even least significant digit. /// @@ -1900,7 +2142,19 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] pub fn nearbyintf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f16::round`](../../std/primitive.f16.html#method.round) + #[rustc_nounwind] + pub fn roundf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. /// /// The stabilized version of this intrinsic is @@ -1913,7 +2167,19 @@ extern "rust-intrinsic" { /// [`f64::round`](../../std/primitive.f64.html#method.round) #[rustc_nounwind] pub fn roundf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f128::round`](../../std/primitive.f128.html#method.round) + #[rustc_nounwind] + pub fn roundf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases to the number /// with an even least significant digit. /// @@ -1926,6 +2192,12 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] pub fn roundevenf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf128(x: f128) -> f128; /// Float addition that allows optimizations based on algebraic rules. /// May assume inputs are finite. @@ -1997,7 +2269,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`]. @@ -2438,20 +2710,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. /// @@ -2466,12 +2724,12 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn catch_unwind(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32; - /// Emits a `!nontemporal` store according to LLVM (see their docs). - /// Probably will never become stable. + /// Emits a `nontemporal` store, which gives a hint to the CPU that the data should not be held + /// in cache. Except for performance, this is fully equivalent to `ptr.write(val)`. /// - /// Do NOT use this intrinsic; "nontemporal" operations do not exist in our memory model! - /// It exists to support current stdarch, but the plan is to change stdarch and remove this intrinsic. - /// See for some more discussion. + /// Not all architectures provide such an operation. For instance, x86 does not: while `MOVNT` + /// exists, that operation is *not* equivalent to `ptr.write(val)` (`MOVNT` writes can be reordered + /// in ways that are not allowed for regular writes). #[rustc_nounwind] pub fn nontemporal_store(ptr: *mut T, val: T); @@ -2516,11 +2774,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")] @@ -2745,7 +3005,7 @@ pub const unsafe fn typed_swap(x: *mut T, y: *mut T) { /// sysroot which is built without ub_checks but with `#[rustc_preserve_ub_checks]`. /// For code that gets monomorphized in the user crate (i.e., generic functions and functions with /// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(ub_checks)` means that -/// assertions are enabled whenever the *user crate* has UB checks enabled. However if the +/// assertions are enabled whenever the *user crate* has UB checks enabled. However, if the /// user has UB checks disabled, the checks will still get optimized out. This intrinsic is /// primarily used by [`ub_checks::assert_unsafe_precondition`]. #[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] @@ -2753,7 +3013,7 @@ pub const unsafe fn typed_swap(x: *mut T, y: *mut T) { #[inline(always)] #[rustc_intrinsic] pub const fn ub_checks() -> bool { - cfg!(debug_assertions) + cfg!(ub_checks) } /// Allocates a block of memory at compile time. @@ -2794,8 +3054,11 @@ 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] @@ -2807,8 +3070,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] @@ -2820,6 +3086,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 diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index 1daf1d723fb95..fb0aa5398a55b 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -247,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", @@ -276,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)*) => { @@ -309,7 +310,7 @@ define!( ); define!( "mir_unwind_cleanup", - /// An unwind action that continues execution in a given basic blok. + /// An unwind action that continues execution in a given basic block. fn UnwindCleanup(goto: BasicBlock) -> UnwindActionArg ); @@ -351,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() diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index 30734c020b39b..5982819809937 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 two vectors. + /// 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 two vectors. + /// Returns the maximum of two vectors, elementwise. /// /// `T` must be a vector of floating-point primitive types. /// @@ -228,11 +228,11 @@ 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. /// - /// `U` must be a **const** array of `i32`s. This means it must either refer to a named + /// `U` must be a **const** array or vector of `u32`s. This means it must either refer to a named /// const or be given as an inline const expression (`const { ... }`). /// /// `V` must be a vector with the same element type as `T` and the same length as `U`. @@ -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. /// @@ -275,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. /// @@ -298,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. /// @@ -320,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. /// @@ -341,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. /// @@ -355,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. /// @@ -365,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. @@ -374,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. /// @@ -384,7 +372,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_mul_ordered(x: T, y: U) -> U; - /// Multiply 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. @@ -393,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. /// @@ -402,7 +390,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn simd_reduce_all(x: T) -> bool; - /// Check if any mask value is true. + /// Checks if any mask value is true. /// /// `T` must be a vector of integer primitive types. /// @@ -411,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. /// @@ -421,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. /// @@ -431,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. /// @@ -439,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. /// @@ -447,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. /// @@ -455,7 +443,7 @@ 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. /// @@ -491,7 +479,7 @@ extern "rust-intrinsic" { #[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. /// @@ -506,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`. /// @@ -523,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. /// @@ -533,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. /// @@ -547,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`. /// @@ -555,56 +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] 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/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 d772e7b36e09e..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}; 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 ba49070329c22..dd08cd6f61c4c 100644 --- a/library/core/src/iter/adapters/filter.rs +++ b/library/core/src/iter/adapters/filter.rs @@ -1,11 +1,13 @@ -use crate::fmt; -use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedFused}; -use crate::num::NonZero; -use crate::ops::Try; use core::array; use core::mem::MaybeUninit; use core::ops::ControlFlow; +use crate::fmt; +use crate::iter::adapters::SourceIter; +use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused}; +use crate::num::NonZero; +use crate::ops::Try; + /// An iterator that filters the elements of `iter` with `predicate`. /// /// This `struct` is created by the [`filter`] method on [`Iterator`]. See its diff --git a/library/core/src/iter/adapters/filter_map.rs b/library/core/src/iter/adapters/filter_map.rs index 2126619a58a87..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}; 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 182775121369e..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. /// diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 1bde4488cc9de..96158c43318ea 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -28,51 +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 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..b96335f415257 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}; @@ -319,3 +317,60 @@ impl SpecTake for Take { } } } + +#[stable(feature = "exact_size_take_repeat", since = "1.82.0")] +impl DoubleEndedIterator for Take> { + #[inline] + fn next_back(&mut self) -> Option { + self.next() + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + self.nth(n) + } + + #[inline] + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + self.try_fold(init, fold) + } + + #[inline] + fn rfold(self, init: Acc, fold: Fold) -> Acc + where + Self: Sized, + Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.fold(init, fold) + } + + #[inline] + #[rustc_inherit_overflow_checks] + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + self.advance_by(n) + } +} + +// Note: It may be tempting to impl DoubleEndedIterator for Take. +// One must fight that temptation since such implementation wouldn’t be correct +// because we have no way to return value of nth invocation of repeater followed +// by n-1st without remembering all results. + +#[stable(feature = "exact_size_take_repeat", since = "1.82.0")] +impl ExactSizeIterator for Take> { + fn len(&self) -> usize { + self.n + } +} + +#[stable(feature = "exact_size_take_repeat", since = "1.82.0")] +impl A, A> ExactSizeIterator for Take> { + fn len(&self) -> usize { + self.n + } +} 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 921c75c85f161..387963d0afd01 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", @@ -406,65 +436,30 @@ pub use self::sources::{once, Once}; pub use self::sources::{once_with, OnceWith}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::sources::{repeat, Repeat}; -#[unstable(feature = "iter_repeat_n", issue = "104434")] +#[stable(feature = "iter_repeat_n", since = "1.82.0")] pub use self::sources::{repeat_n, RepeatN}; #[stable(feature = "iterator_repeat_with", since = "1.28.0")] 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, }; -#[unstable(feature = "iter_chain", reason = "recently added", issue = "125964")] -pub use self::adapters::chain; -#[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..2c726fbca8760 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}; +#[stable(feature = "iter_repeat_n", since = "1.82.0")] +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_n.rs b/library/core/src/iter/sources/repeat_n.rs index 8390dab8e543e..9c0621933638e 100644 --- a/library/core/src/iter/sources/repeat_n.rs +++ b/library/core/src/iter/sources/repeat_n.rs @@ -18,7 +18,6 @@ use crate::num::NonZero; /// Basic usage: /// /// ``` -/// #![feature(iter_repeat_n)] /// use std::iter; /// /// // four of the number four: @@ -36,7 +35,6 @@ use crate::num::NonZero; /// For non-`Copy` types, /// /// ``` -/// #![feature(iter_repeat_n)] /// use std::iter; /// /// let v: Vec = Vec::with_capacity(123); @@ -58,7 +56,7 @@ use crate::num::NonZero; /// assert_eq!(None, it.next()); /// ``` #[inline] -#[unstable(feature = "iter_repeat_n", issue = "104434")] +#[stable(feature = "iter_repeat_n", since = "1.82.0")] pub fn repeat_n(element: T, count: usize) -> RepeatN { let mut element = ManuallyDrop::new(element); @@ -77,7 +75,7 @@ pub fn repeat_n(element: T, count: usize) -> RepeatN { /// This `struct` is created by the [`repeat_n()`] function. /// See its documentation for more. #[derive(Clone, Debug)] -#[unstable(feature = "iter_repeat_n", issue = "104434")] +#[stable(feature = "iter_repeat_n", since = "1.82.0")] pub struct RepeatN { count: usize, // Invariant: has been dropped iff count == 0. @@ -101,32 +99,25 @@ impl RepeatN { } } -#[unstable(feature = "iter_repeat_n", issue = "104434")] +#[stable(feature = "iter_repeat_n", since = "1.82.0")] impl Drop for RepeatN { fn drop(&mut self) { self.take_element(); } } -#[unstable(feature = "iter_repeat_n", issue = "104434")] +#[stable(feature = "iter_repeat_n", since = "1.82.0")] impl Iterator for RepeatN { type Item = A; #[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] @@ -163,14 +154,14 @@ impl Iterator for RepeatN { } } -#[unstable(feature = "iter_repeat_n", issue = "104434")] +#[stable(feature = "iter_repeat_n", since = "1.82.0")] impl ExactSizeIterator for RepeatN { fn len(&self) -> usize { self.count } } -#[unstable(feature = "iter_repeat_n", issue = "104434")] +#[stable(feature = "iter_repeat_n", since = "1.82.0")] impl DoubleEndedIterator for RepeatN { #[inline] fn next_back(&mut self) -> Option { @@ -188,10 +179,24 @@ impl DoubleEndedIterator for RepeatN { } } -#[unstable(feature = "iter_repeat_n", issue = "104434")] +#[stable(feature = "iter_repeat_n", since = "1.82.0")] 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 {} +#[stable(feature = "iter_repeat_n", since = "1.82.0")] +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..5b7d95c2f65e6 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; } @@ -104,7 +104,7 @@ macro_rules! float_sum_product { impl Sum for $a { fn sum>(iter: I) -> Self { iter.fold( - 0.0, + -0.0, #[rustc_inherit_overflow_checks] |a, b| a + b, ) @@ -126,7 +126,7 @@ macro_rules! float_sum_product { impl<'a> Sum<&'a $a> for $a { fn sum>(iter: I) -> Self { iter.fold( - 0.0, + -0.0, #[rustc_inherit_overflow_checks] |a, b| a + b, ) diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 733d414d44465..8352486ad416e 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 /// @@ -3951,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()); @@ -3960,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 = "1.82.0")] #[rustc_do_not_const_check] fn is_sorted(self) -> bool where @@ -3978,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)); /// @@ -3989,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 = "1.82.0")] #[rustc_do_not_const_check] fn is_sorted_by(mut self, compare: F) -> bool where @@ -4030,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 = "1.82.0")] #[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 83cc7e6f65cf5..402c1e05b4400 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -103,12 +103,12 @@ #![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 #![feature(array_ptr_get)] #![feature(asm_experimental_arch)] -#![feature(char_indices_offset)] #![feature(const_align_of_val)] #![feature(const_align_of_val_raw)] #![feature(const_align_offset)] @@ -127,7 +127,6 @@ #![feature(const_hash)] #![feature(const_heap)] #![feature(const_index_range_slice_index)] -#![feature(const_int_from_str)] #![feature(const_intrinsic_copy)] #![feature(const_intrinsic_forget)] #![feature(const_ipv4)] @@ -163,16 +162,16 @@ #![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(is_ascii_octdigit)] +#![feature(is_val_statically_known)] #![feature(isqrt)] #![feature(link_cfg)] #![feature(offset_of_enum)] -#![feature(offset_of_nested)] #![feature(panic_internals)] #![feature(ptr_alignment_type)] #![feature(ptr_metadata)] @@ -192,18 +191,15 @@ // // Language features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(c_unwind))] -#![cfg_attr(bootstrap, feature(effects))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] -#![feature(asm_const)] #![feature(auto_traits)] #![feature(cfg_sanitize)] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_has_atomic_equal_alignment)] -#![feature(const_fn_floating_point_arithmetic)] +#![feature(cfg_ub_checks)] #![feature(const_for)] #![feature(const_mut_refs)] #![feature(const_precise_live_drops)] @@ -227,7 +223,6 @@ #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] #![feature(marker_trait_attr)] -#![feature(min_exhaustive_patterns)] #![feature(min_specialization)] #![feature(multiple_supertrait_upcastable)] #![feature(must_not_suspend)] @@ -235,6 +230,7 @@ #![feature(never_type)] #![feature(no_core)] #![feature(no_sanitize)] +#![feature(optimize_attribute)] #![feature(prelude_import)] #![feature(repr_simd)] #![feature(rustc_allow_const_fn_unstable)] @@ -262,9 +258,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 @@ -391,7 +389,7 @@ pub mod net; pub mod option; pub mod panic; pub mod panicking; -#[unstable(feature = "core_pattern_types", issue = "none")] +#[unstable(feature = "core_pattern_types", issue = "123646")] pub mod pat; pub mod pin; #[unstable(feature = "new_range_api", issue = "125687")] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 0d4ca4d5f01e4..888832251f6da 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`). @@ -1054,7 +1072,7 @@ pub(crate) mod builtin { /// If the environment variable is not defined, then a compilation error /// will be emitted. To not emit a compile error, use the [`option_env!`] /// macro instead. A compilation error will also be emitted if the - /// environment variable is not a vaild Unicode string. + /// environment variable is not a valid Unicode string. /// /// # Examples /// diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 21abd7c036ba7..fd41b80cdbd0a 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 {} @@ -287,8 +288,19 @@ marker_impls! { /// } /// ``` /// -/// There is a small difference between the two: the `derive` strategy will also place a `Copy` -/// bound on type parameters, which isn't always desired. +/// There is a small difference between the two. The `derive` strategy will also place a `Copy` +/// bound on type parameters: +/// +/// ``` +/// #[derive(Clone)] +/// struct MyStruct(T); +/// +/// impl Copy for MyStruct { } +/// ``` +/// +/// This isn't always desired. For example, shared references (`&T`) can be copied regardless of +/// whether `T` is `Copy`. Likewise, a generic struct containing markers such as [`PhantomData`] +/// could potentially be duplicated with a bit-wise copy. /// /// ## What's the difference between `Copy` and `Clone`? /// @@ -869,7 +881,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`: @@ -975,31 +987,64 @@ 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 */ } +#[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`. +#[rustc_builtin_macro] +#[allow_internal_unstable(unsized_const_params)] +#[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, + (), + {T: ConstParamTy_, const N: usize} [T; N], +} + +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, - str /* Technically requires `[u8]: ConstParamTy` */, (), - {T: ConstParamTy, const N: usize} [T; N], - {T: ConstParamTy} [T], - {T: ?Sized + ConstParamTy} &T, + {T: UnsizedConstParamTy, const N: usize} [T; N], + + str, + {T: UnsizedConstParamTy} [T], + {T: UnsizedConstParamTy + ?Sized} &T, } /// A common trait implemented by all function pointers. @@ -1017,8 +1062,7 @@ pub trait FnPtr: Copy + Clone { } /// Derive macro generating impls of traits related to smart pointers. -#[cfg(not(bootstrap))] -#[rustc_builtin_macro] +#[rustc_builtin_macro(SmartPointer, attributes(pointee))] #[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)] #[unstable(feature = "derive_smart_pointer", issue = "123430")] pub macro SmartPointer($item:item) { @@ -1035,7 +1079,6 @@ pub macro SmartPointer($item:item) { reason = "internal module for implementing effects" )] #[allow(missing_debug_implementations)] // these unit structs don't need `Debug` impls. -#[cfg(not(bootstrap))] pub mod effects { #[lang = "EffectsNoRuntime"] pub struct NoRuntime; @@ -1056,7 +1099,6 @@ pub mod effects { pub trait TyCompat {} impl TyCompat for T {} - impl TyCompat for Maybe {} impl TyCompat for T {} #[lang = "EffectsIntersection"] diff --git a/library/core/src/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs index 997f088c6d687..3e47785ee488e 100644 --- a/library/core/src/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -47,6 +47,7 @@ use crate::ptr; #[lang = "manually_drop"] #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] +#[rustc_pub_transparent] pub struct ManuallyDrop { value: T, } @@ -118,10 +119,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 dd40f57dc8707..4be2e5ef1eade 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`. /// @@ -240,6 +237,7 @@ use crate::slice; #[lang = "maybe_uninit"] #[derive(Copy)] #[repr(transparent)] +#[rustc_pub_transparent] pub union MaybeUninit { uninit: (), value: ManuallyDrop, @@ -310,7 +308,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 diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 9d199a8c894c2..2abc99ddeecaa 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -5,13 +5,9 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::clone; -use crate::cmp; -use crate::fmt; -use crate::hash; -use crate::intrinsics; +use crate::alloc::Layout; use crate::marker::DiscriminantKind; -use crate::ptr; +use crate::{clone, cmp, fmt, hash, intrinsics, ptr}; #[cfg(kani)] use crate::kani; @@ -26,7 +22,7 @@ pub use maybe_uninit::MaybeUninit; mod transmutability; #[unstable(feature = "transmutability", issue = "99571")] -pub use transmutability::{Assume, BikeshedIntrinsicFrom}; +pub use transmutability::{Assume, TransmuteFrom}; #[stable(feature = "rust1", since = "1.0.0")] #[doc(inline)] @@ -362,6 +358,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 +511,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) @@ -611,7 +615,7 @@ pub const fn needs_drop() -> bool { /// /// There is no guarantee that an all-zero byte-pattern represents a valid value /// of some type `T`. For example, the all-zero byte-pattern is not a valid value -/// for reference types (`&T`, `&mut T`) and functions pointers. Using `zeroed` +/// for reference types (`&T`, `&mut T`) and function pointers. Using `zeroed` /// on such types causes immediate [undefined behavior][ub] because [the Rust /// compiler assumes][inv] that there always is a valid value in a variable it /// considers initialized. @@ -1240,6 +1244,10 @@ pub trait SizedTypeProperties: Sized { #[doc(hidden)] #[unstable(feature = "sized_type_properties", issue = "none")] const IS_ZST: bool = size_of::() == 0; + + #[doc(hidden)] + #[unstable(feature = "sized_type_properties", issue = "none")] + const LAYOUT: Layout = Layout::new::(); } #[doc(hidden)] #[unstable(feature = "sized_type_properties", issue = "none")] @@ -1323,7 +1331,7 @@ impl SizedTypeProperties for T {} /// # Examples /// /// ``` -/// #![feature(offset_of_enum, 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..7fa3c33439170 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -1,69 +1,375 @@ -use crate::marker::ConstParamTy; +use crate::marker::{ConstParamTy_, UnsizedConstParamTy}; -/// Are values of a type transmutable into values of another type? +/// Marks that `Src` is transmutable into `Self`. /// -/// This trait is implemented on-the-fly by the compiler for types `Src` and `Self` when the bits of -/// any value of type `Self` are safely transmutable into a value of type `Dst`, in a given `Context`, -/// notwithstanding whatever safety checks you have asked the compiler to [`Assume`] are satisfied. +/// # Implementation +/// +/// This trait cannot be implemented explicitly. It is implemented on-the-fly by +/// the compiler for all types `Src` and `Self` such that, given a set of safety +/// obligations on the programmer (see [`Assume`]), the compiler has proved that +/// the bits of a value of type `Src` can be soundly reinterpreted as a `Self`. +/// +/// # Safety +/// +/// If `Dst: TransmuteFrom`, the compiler guarantees that +/// `Src` is soundly *union-transmutable* into a value of type `Dst`, provided +/// that the programmer has guaranteed that the given [`ASSUMPTIONS`](Assume) +/// are satisfied. +/// +/// A union-transmute is any bit-reinterpretation conversion in the form of: +/// +/// ```rust +/// pub unsafe fn transmute_via_union(src: Src) -> Dst { +/// use core::mem::ManuallyDrop; +/// +/// #[repr(C)] +/// union Transmute { +/// src: ManuallyDrop, +/// dst: ManuallyDrop, +/// } +/// +/// let transmute = Transmute { +/// src: ManuallyDrop::new(src), +/// }; +/// +/// let dst = transmute.dst; +/// +/// ManuallyDrop::into_inner(dst) +/// } +/// ``` +/// +/// Note that this construction is more permissive than +/// [`mem::transmute_copy`](super::transmute_copy); union-transmutes permit +/// conversions that extend the bits of `Src` with trailing padding to fill +/// trailing uninitialized bytes of `Self`; e.g.: +/// +/// ```rust +/// #![feature(transmutability)] +/// +/// use core::mem::{Assume, TransmuteFrom}; +/// +/// let src = 42u8; // size = 1 +/// +/// #[repr(C, align(2))] +/// struct Dst(u8); // size = 2 +// +/// let _ = unsafe { +/// >::transmute(src) +/// }; +/// ``` +/// +/// # Caveats +/// +/// ## Portability +/// +/// Implementations of this trait do not provide any guarantee of portability +/// across toolchains, targets or compilations. This trait may be implemented +/// for certain combinations of `Src`, `Self` and `ASSUME` on some toolchains, +/// targets or compilations, but not others. For example, if the layouts of +/// `Src` or `Self` are non-deterministic, the presence or absence of an +/// implementation of this trait may also be non-deterministic. Even if `Src` +/// and `Self` have deterministic layouts (e.g., they are `repr(C)` structs), +/// Rust does not specify the alignments of its primitive integer types, and +/// layouts that involve these types may vary across toolchains, targets or +/// compilations. +/// +/// ## Stability +/// +/// Implementations of this trait do not provide any guarantee of SemVer +/// stability across the crate versions that define the `Src` and `Self` types. +/// If SemVer stability is crucial to your application, you must consult the +/// documentation of `Src` and `Self`s' defining crates. Note that the presence +/// of `repr(C)`, alone, does not carry a safety invariant of SemVer stability. +/// Furthermore, stability does not imply portability. For example, the size of +/// `usize` is stable, but not portable. #[unstable(feature = "transmutability", issue = "99571")] #[lang = "transmute_trait"] #[rustc_deny_explicit_impl(implement_via_object = false)] #[rustc_coinductive] -pub unsafe trait BikeshedIntrinsicFrom +pub unsafe trait TransmuteFrom where Src: ?Sized, { + /// Transmutes a `Src` value into a `Self`. + /// + /// # Safety + /// + /// The safety obligations of the caller depend on the value of `ASSUME`: + /// - If [`ASSUME.alignment`](Assume::alignment), the caller must guarantee + /// that the addresses of references in the returned `Self` satisfy the + /// alignment requirements of their referent types. + /// - If [`ASSUME.lifetimes`](Assume::lifetimes), the caller must guarantee + /// that references in the returned `Self` will not outlive their + /// referents. + /// - If [`ASSUME.safety`](Assume::safety), the returned value might not + /// satisfy the library safety invariants of `Self`, and the caller must + /// guarantee that undefined behavior does not arise from uses of the + /// returned value. + /// - If [`ASSUME.validity`](Assume::validity), the caller must guarantee + /// that `src` is a bit-valid instance of `Self`. + /// + /// When satisfying the above obligations (if any), the caller must *not* + /// assume that this trait provides any inherent guarantee of layout + /// [portability](#portability) or [stability](#stability). + unsafe fn transmute(src: Src) -> Self + where + Src: Sized, + Self: Sized, + { + use super::ManuallyDrop; + + #[repr(C)] + union Transmute { + src: ManuallyDrop, + dst: ManuallyDrop, + } + + let transmute = Transmute { src: ManuallyDrop::new(src) }; + + // SAFETY: It is safe to reinterpret the bits of `src` as a value of + // type `Self`, because, by combination of invariant on this trait and + // contract on the caller, `src` has been proven to satisfy both the + // language and library invariants of `Self`. For all invariants not + // `ASSUME`'d by the caller, the safety obligation is supplied by the + // compiler. Conversely, for all invariants `ASSUME`'d by the caller, + // the safety obligation is supplied by contract on the caller. + let dst = unsafe { transmute.dst }; + + ManuallyDrop::into_inner(dst) + } } -/// What transmutation safety conditions shall the compiler assume that *you* are checking? +/// Configurable proof assumptions of [`TransmuteFrom`]. +/// +/// When `false`, the respective proof obligation belongs to the compiler. When +/// `true`, the onus of the safety proof belongs to the programmer. #[unstable(feature = "transmutability", issue = "99571")] #[lang = "transmute_opts"] #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct Assume { - /// When `true`, the compiler assumes that *you* are ensuring (either dynamically or statically) that - /// destination referents do not have stricter alignment requirements than source referents. + /// When `false`, [`TransmuteFrom`] is not implemented for transmutations + /// that might violate the alignment requirements of references; e.g.: + /// + /// ```compile_fail,E0277 + /// #![feature(transmutability)] + /// use core::mem::{align_of, TransmuteFrom}; + /// + /// assert_eq!(align_of::<[u8; 2]>(), 1); + /// assert_eq!(align_of::(), 2); + /// + /// let src: &[u8; 2] = &[0xFF, 0xFF]; + /// + /// // SAFETY: No safety obligations. + /// let dst: &u16 = unsafe { + /// <_ as TransmuteFrom<_>>::transmute(src) + /// }; + /// ``` + /// + /// When `true`, [`TransmuteFrom`] assumes that *you* have ensured + /// that references in the transmuted value satisfy the alignment + /// requirements of their referent types; e.g.: + /// + /// ```rust + /// #![feature(pointer_is_aligned_to, transmutability)] + /// use core::mem::{align_of, Assume, TransmuteFrom}; + /// + /// let src: &[u8; 2] = &[0xFF, 0xFF]; + /// + /// let maybe_dst: Option<&u16> = if <*const _>::is_aligned_to(src, align_of::()) { + /// // SAFETY: We have checked above that the address of `src` satisfies the + /// // alignment requirements of `u16`. + /// Some(unsafe { + /// <_ as TransmuteFrom<_, { Assume::ALIGNMENT }>>::transmute(src) + /// }) + /// } else { + /// None + /// }; + /// + /// assert!(matches!(maybe_dst, Some(&u16::MAX) | None)); + /// ``` pub alignment: bool, - /// When `true`, the compiler assume that *you* are ensuring that lifetimes are not extended in a manner - /// that violates Rust's memory model. + /// When `false`, [`TransmuteFrom`] is not implemented for transmutations + /// that extend the lifetimes of references. + /// + /// When `true`, [`TransmuteFrom`] assumes that *you* have ensured that + /// references in the transmuted value do not outlive their referents. pub lifetimes: bool, - /// When `true`, the compiler assumes that *you* have ensured that no - /// unsoundness will arise from violating the safety invariants of the - /// destination type (and sometimes of the source type, too). + /// When `false`, [`TransmuteFrom`] is not implemented for transmutations + /// that might violate the library safety invariants of the destination + /// type; e.g.: + /// + /// ```compile_fail,E0277 + /// #![feature(transmutability)] + /// use core::mem::TransmuteFrom; + /// + /// let src: u8 = 3; + /// + /// struct EvenU8 { + /// // SAFETY: `val` must be an even number. + /// val: u8, + /// } + /// + /// // SAFETY: No safety obligations. + /// let dst: EvenU8 = unsafe { + /// <_ as TransmuteFrom<_>>::transmute(src) + /// }; + /// ``` + /// + /// When `true`, [`TransmuteFrom`] assumes that *you* have ensured + /// that undefined behavior does not arise from using the transmuted value; + /// e.g.: + /// + /// ```rust + /// #![feature(transmutability)] + /// use core::mem::{Assume, TransmuteFrom}; + /// + /// let src: u8 = 42; + /// + /// struct EvenU8 { + /// // SAFETY: `val` must be an even number. + /// val: u8, + /// } + /// + /// let maybe_dst: Option = if src % 2 == 0 { + /// // SAFETY: We have checked above that the value of `src` is even. + /// Some(unsafe { + /// <_ as TransmuteFrom<_, { Assume::SAFETY }>>::transmute(src) + /// }) + /// } else { + /// None + /// }; + /// + /// assert!(matches!(maybe_dst, Some(EvenU8 { val: 42 }))); + /// ``` pub safety: bool, - /// When `true`, the compiler assumes that *you* are ensuring that the source type is actually a valid - /// instance of the destination type. + /// When `false`, [`TransmuteFrom`] is not implemented for transmutations + /// that might violate the language-level bit-validity invariant of the + /// destination type; e.g.: + /// + /// ```compile_fail,E0277 + /// #![feature(transmutability)] + /// use core::mem::TransmuteFrom; + /// + /// let src: u8 = 3; + /// + /// // SAFETY: No safety obligations. + /// let dst: bool = unsafe { + /// <_ as TransmuteFrom<_>>::transmute(src) + /// }; + /// ``` + /// + /// When `true`, [`TransmuteFrom`] assumes that *you* have ensured + /// that the value being transmuted is a bit-valid instance of the + /// transmuted value; e.g.: + /// + /// ```rust + /// #![feature(transmutability)] + /// use core::mem::{Assume, TransmuteFrom}; + /// + /// let src: u8 = 1; + /// + /// let maybe_dst: Option = if src == 0 || src == 1 { + /// // SAFETY: We have checked above that the value of `src` is a bit-valid + /// // instance of `bool`. + /// Some(unsafe { + /// <_ as TransmuteFrom<_, { Assume::VALIDITY }>>::transmute(src) + /// }) + /// } else { + /// None + /// }; + /// + /// assert_eq!(maybe_dst, Some(true)); + /// ``` pub validity: bool, } #[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. + /// With this, [`TransmuteFrom`] does not assume you have ensured any safety + /// obligations are met, and relies only upon its own analysis to (dis)prove + /// transmutability. #[unstable(feature = "transmutability", issue = "99571")] pub const NOTHING: Self = Self { alignment: false, lifetimes: false, safety: false, validity: false }; - /// Assume only that alignment conditions are met. + /// With this, [`TransmuteFrom`] assumes only that you have ensured that + /// references in the transmuted value satisfy the alignment requirements of + /// their referent types. See [`Assume::alignment`] for examples. #[unstable(feature = "transmutability", issue = "99571")] pub const ALIGNMENT: Self = Self { alignment: true, ..Self::NOTHING }; - /// Assume only that lifetime conditions are met. + /// With this, [`TransmuteFrom`] assumes only that you have ensured that + /// references in the transmuted value do not outlive their referents. See + /// [`Assume::lifetimes`] for examples. #[unstable(feature = "transmutability", issue = "99571")] pub const LIFETIMES: Self = Self { lifetimes: true, ..Self::NOTHING }; - /// Assume only that safety conditions are met. + /// With this, [`TransmuteFrom`] assumes only that you have ensured that + /// undefined behavior does not arise from using the transmuted value. See + /// [`Assume::safety`] for examples. #[unstable(feature = "transmutability", issue = "99571")] pub const SAFETY: Self = Self { safety: true, ..Self::NOTHING }; - /// Assume only that dynamically-satisfiable validity conditions are met. + /// With this, [`TransmuteFrom`] assumes only that you have ensured that the + /// value being transmuted is a bit-valid instance of the transmuted value. + /// See [`Assume::validity`] for examples. #[unstable(feature = "transmutability", issue = "99571")] pub const VALIDITY: Self = Self { validity: true, ..Self::NOTHING }; - /// Assume both `self` and `other_assumptions`. + /// Combine the assumptions of `self` and `other_assumptions`. + /// + /// This is especially useful for extending [`Assume`] in generic contexts; + /// e.g.: + /// + /// ```rust + /// #![feature( + /// adt_const_params, + /// generic_const_exprs, + /// pointer_is_aligned_to, + /// transmutability, + /// )] + /// #![allow(incomplete_features)] + /// use core::mem::{align_of, Assume, TransmuteFrom}; + /// + /// /// Attempts to transmute `src` to `&Dst`. + /// /// + /// /// Returns `None` if `src` violates the alignment requirements of `&Dst`. + /// /// + /// /// # Safety + /// /// + /// /// The caller guarantees that the obligations required by `ASSUME`, except + /// /// alignment, are satisfied. + /// unsafe fn try_transmute_ref<'a, Src, Dst, const ASSUME: Assume>(src: &'a Src) -> Option<&'a Dst> + /// where + /// &'a Dst: TransmuteFrom<&'a Src, { ASSUME.and(Assume::ALIGNMENT) }>, + /// { + /// if <*const _>::is_aligned_to(src, align_of::()) { + /// // SAFETY: By the above dynamic check, we have ensured that the address + /// // of `src` satisfies the alignment requirements of `&Dst`. By contract + /// // on the caller, the safety obligations required by `ASSUME` have also + /// // been satisfied. + /// Some(unsafe { + /// <_ as TransmuteFrom<_, { ASSUME.and(Assume::ALIGNMENT) }>>::transmute(src) + /// }) + /// } else { + /// None + /// } + /// } + /// + /// let src: &[u8; 2] = &[0xFF, 0xFF]; + /// + /// // SAFETY: No safety obligations. + /// let maybe_dst: Option<&u16> = unsafe { + /// try_transmute_ref::<_, _, { Assume::NOTHING }>(src) + /// }; + ///``` #[unstable(feature = "transmutability", issue = "99571")] pub const fn and(self, other_assumptions: Self) -> Self { Self { @@ -74,7 +380,20 @@ impl Assume { } } - /// Assume `self`, excepting `other_assumptions`. + /// Remove `other_assumptions` the obligations of `self`; e.g.: + /// + /// ```rust + /// #![feature(transmutability)] + /// use core::mem::Assume; + /// + /// let assumptions = Assume::ALIGNMENT.and(Assume::SAFETY); + /// let to_be_removed = Assume::SAFETY.and(Assume::VALIDITY); + /// + /// assert_eq!( + /// assumptions.but_not(to_be_removed), + /// Assume::ALIGNMENT, + /// ); + /// ``` #[unstable(feature = "transmutability", issue = "99571")] pub const fn but_not(self, other_assumptions: Self) -> Self { Self { diff --git a/library/core/src/net/display_buffer.rs b/library/core/src/net/display_buffer.rs index 6619c85f483ef..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 { diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index c11a508a135b3..919f681f911f9 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -1,11 +1,11 @@ +use super::display_buffer::DisplayBuffer; use crate::cmp::Ordering; use crate::fmt::{self, Write}; +use crate::hash::{Hash, Hasher}; 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 @@ -68,12 +68,22 @@ pub enum IpAddr { /// assert!("0000000.0.0.0".parse::().is_err()); // first octet is a zero in octal /// assert!("0xcb.0x0.0x71.0x00".parse::().is_err()); // all octets are in hex /// ``` -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Ipv4Addr { octets: [u8; 4], } +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for Ipv4Addr { + fn hash(&self, state: &mut H) { + // Hashers are often more efficient at hashing a fixed-width integer + // than a bytestring, so convert before hashing. We don't use to_bits() + // here as that may involve a byteswap which is unnecessary. + u32::from_ne_bytes(self.octets).hash(state); + } +} + /// An IPv6 address. /// /// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291]. @@ -150,12 +160,22 @@ pub struct Ipv4Addr { /// assert_eq!("::1".parse(), Ok(localhost)); /// assert_eq!(localhost.is_loopback(), true); /// ``` -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Ipv6Addr { octets: [u8; 16], } +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for Ipv6Addr { + fn hash(&self, state: &mut H) { + // Hashers are often more efficient at hashing a fixed-width integer + // than a bytestring, so convert before hashing. We don't use to_bits() + // here as that may involve unnecessary byteswaps. + u128::from_ne_bytes(self.octets).hash(state); + } +} + /// Scope of an [IPv6 multicast address] as defined in [IETF RFC 7346 section 2]. /// /// # Stability Guarantees @@ -406,8 +426,8 @@ impl IpAddr { matches!(self, IpAddr::V6(_)) } - /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped IPv6 address, 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 /// @@ -549,7 +569,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 /// @@ -686,10 +706,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: /// @@ -802,8 +822,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 @@ -827,10 +849,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 /// @@ -1328,7 +1352,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. /// @@ -1424,10 +1448,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)) @@ -1879,8 +1903,8 @@ impl Ipv6Addr { } } - /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped address, 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 /// @@ -1919,7 +1943,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 { @@ -1962,7 +1986,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..73230f6ee5b03 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 }) @@ -112,18 +112,18 @@ impl<'a> Parser<'a> { max_digits: Option, allow_zero_prefix: bool, ) -> Option { - // If max_digits.is_some(), then we are parsing a `u8` or `u16` and - // don't need to use checked arithmetic since it fits within a `u32`. - if let Some(max_digits) = max_digits { - // u32::MAX = 4_294_967_295u32, which is 10 digits long. - // `max_digits` must be less than 10 to not overflow a `u32`. - debug_assert!(max_digits < 10); - - self.read_atomically(move |p| { - let mut result = 0_u32; - let mut digit_count = 0; - let has_leading_zero = p.peek_char() == Some('0'); + self.read_atomically(move |p| { + let mut digit_count = 0; + let has_leading_zero = p.peek_char() == Some('0'); + + // If max_digits.is_some(), then we are parsing a `u8` or `u16` and + // don't need to use checked arithmetic since it fits within a `u32`. + let result = if let Some(max_digits) = max_digits { + // u32::MAX = 4_294_967_295u32, which is 10 digits long. + // `max_digits` must be less than 10 to not overflow a `u32`. + debug_assert!(max_digits < 10); + let mut result = 0_u32; while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { result *= radix; result += digit; @@ -134,19 +134,9 @@ impl<'a> Parser<'a> { } } - if digit_count == 0 { - None - } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 { - None - } else { - result.try_into().ok() - } - }) - } else { - self.read_atomically(move |p| { + result.try_into().ok() + } else { let mut result = T::ZERO; - let mut digit_count = 0; - let has_leading_zero = p.peek_char() == Some('0'); while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { result = result.checked_mul(radix)?; @@ -154,18 +144,20 @@ impl<'a> Parser<'a> { digit_count += 1; } - if digit_count == 0 { - None - } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 { - None - } else { - Some(result) - } - }) - } + Some(result) + }; + + if digit_count == 0 { + None + } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 { + None + } else { + result + } + }) } - /// 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 +174,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 +241,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 +254,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 +262,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 +271,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 +284,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 c85727b493816..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. 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/mod.rs b/library/core/src/num/dec2flt/mod.rs index 9aac2332dce0d..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; diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index a2d7e6f7b0754..6ef2fdd14c149 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 = "1.82.0")] #[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 05dc1e97852e0..d4236e47bfe3b 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -234,24 +234,20 @@ impl f128 { /// 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. - #[cfg(not(bootstrap))] #[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 (∞). - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] pub const INFINITY: f128 = 1.0_f128 / 0.0_f128; /// Negative infinity (−∞). - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] pub const NEG_INFINITY: f128 = -1.0_f128 / 0.0_f128; /// Sign bit - #[cfg(not(bootstrap))] pub(crate) const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; /// Exponent mask @@ -261,11 +257,9 @@ impl f128 { pub(crate) const MAN_MASK: u128 = 0x0000_ffff_ffff_ffff_ffff_ffff_ffff_ffff; /// Minimum representable positive value (min subnormal) - #[cfg(not(bootstrap))] const TINY_BITS: u128 = 0x1; /// Minimum representable negative value (min negative subnormal) - #[cfg(not(bootstrap))] const NEG_TINY_BITS: u128 = Self::TINY_BITS | Self::SIGN_MASK; /// Returns `true` if this value is NaN. @@ -284,7 +278,6 @@ impl f128 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :) pub const fn is_nan(self) -> bool { @@ -295,10 +288,9 @@ impl f128 { // concerns about portability, so this implementation is for // private use internally. #[inline] - #[cfg(not(bootstrap))] #[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. + // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`. unsafe { mem::transmute::(mem::transmute::(self) & !Self::SIGN_MASK) } @@ -326,7 +318,6 @@ impl f128 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_infinite(self) -> bool { @@ -354,7 +345,6 @@ impl f128 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_finite(self) -> bool { @@ -389,7 +379,6 @@ impl f128 { /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_subnormal(self) -> bool { @@ -422,7 +411,6 @@ impl f128 { /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_normal(self) -> bool { @@ -448,26 +436,15 @@ impl f128 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[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. + // Other float types suffer from various platform bugs that violate the usual IEEE semantics + // and also make bitwise classification not always work reliably. However, `f128` cannot fit + // into any other float types so this is not a concern, and we can 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) { + let bits = self.to_bits(); + match (bits & Self::MAN_MASK, bits & Self::EXP_MASK) { (0, Self::EXP_MASK) => FpCategory::Infinite, (_, Self::EXP_MASK) => FpCategory::Nan, (0, 0) => FpCategory::Zero, @@ -477,11 +454,14 @@ impl f128 { } /// 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](f128) 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 or non-portable result. See the [specification + /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// #![feature(f128)] @@ -500,11 +480,14 @@ impl f128 { } /// 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](f128) 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 or non-portable result. See the [specification + /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// #![feature(f128)] @@ -557,7 +540,6 @@ impl f128 { /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] // #[unstable(feature = "float_next_up_down", issue = "91399")] pub fn next_up(self) -> Self { @@ -612,7 +594,6 @@ impl f128 { /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] // #[unstable(feature = "float_next_up_down", issue = "91399")] pub fn next_down(self) -> Self { @@ -649,7 +630,6 @@ impl f128 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub fn recip(self) -> Self { @@ -670,7 +650,6 @@ impl f128 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_degrees(self) -> Self { @@ -694,7 +673,6 @@ impl f128 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_radians(self) -> f128 { @@ -704,6 +682,182 @@ impl f128 { self * RADS_PER_DEG } + /// Returns the maximum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. + /// This also matches the behavior of libm’s fmax. + /// + /// ``` + /// #![feature(f128)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.max(y), y); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn max(self, other: f128) -> f128 { + intrinsics::maxnumf128(self, other) + } + + /// Returns the minimum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids minNum's problems with associativity. + /// This also matches the behavior of libm’s fmin. + /// + /// ``` + /// #![feature(f128)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.min(y), x); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn min(self, other: f128) -> f128 { + intrinsics::minnumf128(self, other) + } + + /// Returns the maximum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f128::max`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_minimum_maximum)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.maximum(y), y); + /// assert!(x.maximum(f128::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn maximum(self, other: f128) -> f128 { + if self > other { + self + } else if other > self { + other + } else if self == other { + if self.is_sign_positive() && other.is_sign_negative() { self } else { other } + } else { + self + other + } + } + + /// Returns the minimum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f128::min`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_minimum_maximum)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.minimum(y), x); + /// assert!(x.minimum(f128::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn minimum(self, other: f128) -> f128 { + if self < other { + self + } else if other < self { + other + } else if self == other { + if self.is_sign_negative() && other.is_sign_positive() { self } else { other } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + self + other + } + } + + /// Calculates the middle point of `self` and `rhs`. + /// + /// This returns NaN when *either* argument is NaN or if a combination of + /// +inf and -inf is provided as arguments. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(num_midpoint)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// assert_eq!(1f128.midpoint(4.0), 2.5); + /// assert_eq!((-5.5f128).midpoint(8.0), 1.25); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f128) -> f128 { + const LO: f128 = f128::MIN_POSITIVE * 2.; + const HI: f128 = f128::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` (would underflow) + a + (b / 2.) + } else if abs_b < LO { + // Not safe to halve `b` (would underflow) + (a / 2.) + b + } else { + // Safe to halve `a` and `b` + (a / 2.) + (b / 2.) + } + } + /// Rounds toward zero and converts to any primitive integer type, /// assuming that the value is finite and fits in that type. /// @@ -764,48 +918,7 @@ impl f128 { #[must_use = "this returns the result of the operation, without modifying the original"] 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) + unsafe { mem::transmute(self) } } /// Raw transmutation from `u128`. @@ -853,52 +966,11 @@ impl f128 { #[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) + // SAFETY: `u128` is a plain old datatype so we can always transmute from it. + unsafe { mem::transmute(v) } } - /// 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 @@ -924,7 +996,7 @@ impl f128 { 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 @@ -950,7 +1022,7 @@ impl f128 { 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 @@ -987,7 +1059,7 @@ impl f128 { 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). @@ -1014,7 +1086,7 @@ impl f128 { Self::from_bits(u128::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). @@ -1041,7 +1113,7 @@ impl f128 { Self::from_bits(u128::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 @@ -1078,7 +1150,7 @@ impl f128 { Self::from_bits(u128::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 @@ -1141,7 +1213,6 @@ impl f128 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f128", issue = "116909")] pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { let mut left = self.to_bits() as i128; @@ -1201,7 +1272,6 @@ impl f128 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[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 { diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 2a8ede9383844..1e2f841aca733 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -229,24 +229,20 @@ impl f16 { /// 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. - #[cfg(not(bootstrap))] #[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 (∞). - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] pub const INFINITY: f16 = 1.0_f16 / 0.0_f16; /// Negative infinity (−∞). - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] pub const NEG_INFINITY: f16 = -1.0_f16 / 0.0_f16; /// Sign bit - #[cfg(not(bootstrap))] pub(crate) const SIGN_MASK: u16 = 0x8000; /// Exponent mask @@ -256,18 +252,16 @@ impl f16 { pub(crate) const MAN_MASK: u16 = 0x03ff; /// Minimum representable positive value (min subnormal) - #[cfg(not(bootstrap))] const TINY_BITS: u16 = 0x1; /// Minimum representable negative value (min negative subnormal) - #[cfg(not(bootstrap))] 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 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let nan = f16::NAN; /// let f = 7.0_f16; @@ -278,7 +272,6 @@ impl f16 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[allow(clippy::eq_op)] // > if you intended to check if the operand is NaN, use `.is_nan()` instead :) pub const fn is_nan(self) -> bool { @@ -289,10 +282,9 @@ impl f16 { // concerns about portability, so this implementation is for // private use internally. #[inline] - #[cfg(not(bootstrap))] #[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. + // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`. unsafe { mem::transmute::(mem::transmute::(self) & !Self::SIGN_MASK) } } @@ -301,7 +293,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let f = 7.0f16; /// let inf = f16::INFINITY; @@ -317,7 +309,6 @@ impl f16 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_infinite(self) -> bool { @@ -328,7 +319,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let f = 7.0f16; /// let inf: f16 = f16::INFINITY; @@ -344,7 +335,6 @@ impl f16 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_finite(self) -> bool { @@ -357,7 +347,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let min = f16::MIN_POSITIVE; // 6.1035e-5 /// let max = f16::MAX; @@ -377,7 +367,6 @@ impl f16 { /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_subnormal(self) -> bool { @@ -388,7 +377,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let min = f16::MIN_POSITIVE; // 6.1035e-5 /// let max = f16::MAX; @@ -408,7 +397,6 @@ impl f16 { /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] pub const fn is_normal(self) -> bool { @@ -421,7 +409,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// use std::num::FpCategory; /// @@ -433,21 +421,20 @@ impl f16 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[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`. + // If we only cared about being "technically" correct, that's an entirely legit + // implementation. // - - // 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. + // Unfortunately, there are platforms out there that do not correctly implement the IEEE + // float semantics Rust relies on: some hardware flushes denormals to zero, and some + // platforms convert to `f32` to perform operations without properly rounding back (e.g. + // WASM, see llvm/llvm-project#96437). These are platforms bugs, and Rust will misbehave on + // such platforms, but we can at least try to make things seem as sane as possible by being + // careful here. if self.is_infinite() { // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask. FpCategory::Infinite @@ -459,59 +446,32 @@ impl f16 { // 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] - #[cfg(not(bootstrap))] - #[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, + // So, this does use bitpattern matching for the rest. On x87, due to the incorrect + // float codegen on this hardware, this doesn't actually return a right answer for NaN + // because it cannot correctly discern between a floating point NaN, and some normal + // floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so + // we are fine. + // FIXME(jubilee): This probably could at least answer things correctly for Infinity, + // like the f64 version does, but I need to run more checks on how things go on x86. + // I fear losing mantissa data that would have answered that differently. + let b = self.to_bits(); + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { + (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](f16) 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 or non-portable result. See the [specification + /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// #![feature(f16)] @@ -533,11 +493,14 @@ impl f16 { } /// 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](f16) 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 or non-portable result. See the [specification + /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// #![feature(f16)] @@ -593,7 +556,6 @@ impl f16 { /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] // #[unstable(feature = "float_next_up_down", issue = "91399")] pub fn next_up(self) -> Self { @@ -648,7 +610,6 @@ impl f16 { /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] // #[unstable(feature = "float_next_up_down", issue = "91399")] pub fn next_down(self) -> Self { @@ -685,7 +646,6 @@ impl f16 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub fn recip(self) -> Self { @@ -706,7 +666,6 @@ impl f16 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_degrees(self) -> Self { @@ -730,7 +689,6 @@ impl f16 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub fn to_radians(self) -> f16 { @@ -739,12 +697,183 @@ impl f16 { self * RADS_PER_DEG } + /// Returns the maximum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. + /// This also matches the behavior of libm’s fmax. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.max(y), y); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn max(self, other: f16) -> f16 { + intrinsics::maxnumf16(self, other) + } + + /// Returns the minimum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids minNum's problems with associativity. + /// This also matches the behavior of libm’s fmin. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.min(y), x); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn min(self, other: f16) -> f16 { + intrinsics::minnumf16(self, other) + } + + /// Returns the maximum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f16::max`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_minimum_maximum)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.maximum(y), y); + /// assert!(x.maximum(f16::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn maximum(self, other: f16) -> f16 { + if self > other { + self + } else if other > self { + other + } else if self == other { + if self.is_sign_positive() && other.is_sign_negative() { self } else { other } + } else { + self + other + } + } + + /// Returns the minimum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f16::min`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_minimum_maximum)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.minimum(y), x); + /// assert!(x.minimum(f16::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn minimum(self, other: f16) -> f16 { + if self < other { + self + } else if other < self { + other + } else if self == other { + if self.is_sign_negative() && other.is_sign_positive() { self } else { other } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + self + other + } + } + + /// Calculates the middle point of `self` and `rhs`. + /// + /// This returns NaN when *either* argument is NaN or if a combination of + /// +inf and -inf is provided as arguments. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(num_midpoint)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// assert_eq!(1f16.midpoint(4.0), 2.5); + /// assert_eq!((-5.5f16).midpoint(8.0), 1.25); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f16) -> f16 { + const LO: f16 = f16::MIN_POSITIVE * 2.; + const HI: f16 = f16::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` (would underflow) + a + (b / 2.) + } else if abs_b < LO { + // Not safe to halve `b` (would underflow) + (a / 2.) + b + } else { + // Safe to halve `a` and `b` + (a / 2.) + (b / 2.) + } + } + /// 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 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let value = 4.6_f16; /// let rounded = unsafe { value.to_int_unchecked::() }; @@ -787,7 +916,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// # // FIXME(f16_f128): enable this once const casting works /// # // assert_ne!((1f16).to_bits(), 1f16 as u128); // to_bits() is not casting! @@ -800,48 +929,7 @@ impl f16 { #[must_use = "this returns the result of the operation, without modifying the original"] 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) + unsafe { mem::transmute(self) } } /// Raw transmutation from `u16`. @@ -876,7 +964,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let v = f16::from_bits(0x4a40); /// assert_eq!(v, 12.5); @@ -888,52 +976,11 @@ impl f16 { #[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) + // SAFETY: `u16` is a plain old datatype so we can always transmute from it. + unsafe { mem::transmute(v) } } - /// 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 @@ -958,7 +1005,7 @@ impl f16 { 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 @@ -983,7 +1030,7 @@ impl f16 { 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 @@ -1021,7 +1068,7 @@ impl f16 { 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). @@ -1030,7 +1077,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let value = f16::from_be_bytes([0x4a, 0x40]); /// assert_eq!(value, 12.5); @@ -1044,7 +1091,7 @@ impl f16 { Self::from_bits(u16::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). @@ -1053,7 +1100,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let value = f16::from_le_bytes([0x40, 0x4a]); /// assert_eq!(value, 12.5); @@ -1067,7 +1114,7 @@ impl f16 { Self::from_bits(u16::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 @@ -1083,7 +1130,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let value = f16::from_ne_bytes(if cfg!(target_endian = "big") { /// [0x4a, 0x40] @@ -1101,7 +1148,7 @@ impl f16 { Self::from_bits(u16::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 @@ -1167,7 +1214,6 @@ impl f16 { /// ``` #[inline] #[must_use] - #[cfg(not(bootstrap))] #[unstable(feature = "f16", issue = "116909")] pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { let mut left = self.to_bits() as i16; @@ -1217,7 +1263,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// assert!((-3.0f16).clamp(-2.0, 1.0) == -2.0); /// assert!((0.0f16).clamp(-2.0, 1.0) == 0.0); @@ -1226,7 +1272,6 @@ impl f16 { /// # } /// ``` #[inline] - #[cfg(not(bootstrap))] #[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 { diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index b9c84a66ed138..c1adcc753f2e5 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -529,7 +529,7 @@ impl f32 { #[inline] #[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. + // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`. unsafe { mem::transmute::(mem::transmute::(self) & !Self::SIGN_MASK) } } @@ -654,18 +654,20 @@ impl f32 { pub const fn classify(self) -> FpCategory { // A previous implementation tried to only use bitmask-based checks, // using f32::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 f64::to_bits. + // If we only cared about being "technically" correct, that's an entirely legit + // implementation. + // + // Unfortunately, there is hardware out there that does not correctly implement the IEEE + // float semantics Rust relies on: x87 uses a too-large mantissa and exponent, and some + // hardware flushes subnormals to zero. These are platforms bugs, and Rust will misbehave on + // such hardware, but we can at least try to make things seem as sane as possible by being + // careful here. // - // On some processors, in some cases, LLVM will "helpfully" lower floating point ops, - // in spite of a request for them using f32 and f64, to things like x87 operations. - // These have an f64's mantissa, but can have a larger than normal exponent. // FIXME(jubilee): Using x87 operations is never necessary in order to function // on x86 processors for Rust-to-Rust calls, so this issue should not happen. // Code generation should be adjusted to use non-C calling conventions, avoiding this. - // if self.is_infinite() { - // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask. + // 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. @@ -675,57 +677,32 @@ impl f32 { // 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: f32 to u32 is fine. Usually. - // If classify has gotten this far, the value is definitely in one of these categories. - unsafe { f32::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. - // FIXME(jubilee): This probably could at least answer things correctly for Infinity, - // like the f64 version does, but I need to run more checks on how things go on x86. - // I fear losing mantissa data that would have answered that differently. - // - // # 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. - #[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, 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. - #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] - const fn classify_bits(b: u32) -> 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, + // So, this does use bitpattern matching for the rest. On x87, due to the incorrect + // float codegen on this hardware, this doesn't actually return a right answer for NaN + // because it cannot correctly discern between a floating point NaN, and some normal + // floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so + // we are fine. + // FIXME(jubilee): This probably could at least answer things correctly for Infinity, + // like the f64 version does, but I need to run more checks on how things go on x86. + // I fear losing mantissa data that would have answered that differently. + let b = self.to_bits(); + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { + (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. + /// 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 or non-portable result. See the [specification + /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// let f = 7.0_f32; @@ -743,11 +720,14 @@ 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 or non-portable result. See the [specification + /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// let f = 7.0f32; @@ -793,6 +773,7 @@ impl f32 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX + #[inline] #[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 { @@ -841,6 +822,7 @@ impl f32 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX + #[inline] #[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 { @@ -974,7 +956,7 @@ impl f32 { /// Note that this follows the semantics specified in IEEE 754-2019. /// /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see [explanation of NaN as a special value](f32) for more info. + /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] @@ -1009,7 +991,7 @@ impl f32 { /// Note that this follows the semantics specified in IEEE 754-2019. /// /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see [explanation of NaN as a special value](f32) for more info. + /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] @@ -1038,6 +1020,7 @@ impl f32 { /// assert_eq!(1f32.midpoint(4.0), 2.5); /// assert_eq!((-5.5f32).midpoint(8.0), 1.25); /// ``` + #[inline] #[unstable(feature = "num_midpoint", issue = "110840")] pub fn midpoint(self, other: f32) -> f32 { cfg_if! { @@ -1066,13 +1049,13 @@ impl f32 { // Overflow is impossible (a + b) / 2. } else if abs_a < LO { - // Not safe to halve a + // Not safe to halve `a` (would underflow) a + (b / 2.) } else if abs_b < LO { - // Not safe to halve b + // Not safe to halve `b` (would underflow) (a / 2.) + b } else { - // Not safe to halve a and b + // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } @@ -1136,51 +1119,7 @@ impl f32 { #[inline] pub const fn to_bits(self) -> u32 { // SAFETY: `u32` 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". - // This is not a problem per se, but at least one tier2 platform for Rust - // actually exhibits this behavior by default. - // - // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled, - // i.e. not soft-float, the way Rust does parameter passing can actually alter - // a number that is "not infinity" to have the same exponent as infinity, - // in a slightly unpredictable manner. - // - // 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. - #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] - const fn ct_f32_to_u32(ct: f32) -> u32 { - match ct.classify() { - FpCategory::Nan => { - panic!("const-eval error: cannot use f32::to_bits on a NaN") - } - FpCategory::Subnormal => { - panic!("const-eval error: cannot use f32::to_bits on a subnormal number") - } - FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => { - // SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy. - unsafe { mem::transmute::(ct) } - } - } - } - - #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491 - fn rt_f32_to_u32(x: f32) -> u32 { - // SAFETY: `u32` 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_f32_to_u32, rt_f32_to_u32) + unsafe { mem::transmute(self) } } /// Raw transmutation from `u32`. @@ -1225,56 +1164,11 @@ impl f32 { #[inline] pub const fn from_bits(v: u32) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! - // SAFETY: `u32` 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" - // - // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled, - // i.e. not soft-float, the way Rust does parameter passing can actually alter - // a number that is "not infinity" to have the same exponent as infinity, - // in a slightly unpredictable manner. - // - // 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. - #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] - const fn ct_u32_to_f32(ct: u32) -> f32 { - match f32::classify_bits(ct) { - FpCategory::Subnormal => { - panic!("const-eval error: cannot use f32::from_bits on a subnormal number") - } - FpCategory::Nan => { - panic!("const-eval error: cannot use f32::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_u32_to_f32(x: u32) -> f32 { - // SAFETY: `u32` 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_u32_to_f32, rt_u32_to_f32) + // SAFETY: `u32` is a plain old datatype so we can always transmute from it. + unsafe { mem::transmute(v) } } - /// 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 @@ -1295,7 +1189,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 @@ -1316,7 +1210,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 @@ -1350,7 +1244,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). @@ -1369,7 +1263,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). @@ -1388,7 +1282,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 @@ -1418,7 +1312,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 f8e4555fc44f2..e6406771ad333 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -528,7 +528,7 @@ impl f64 { #[inline] #[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. + // SAFETY: This transmutation is fine just like in `to_bits`/`from_bits`. unsafe { mem::transmute::(mem::transmute::(self) & !Self::SIGN_MASK) } } @@ -653,12 +653,14 @@ impl f64 { pub const fn classify(self) -> FpCategory { // A previous implementation tried to only use bitmask-based checks, // using f64::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 f64::to_bits. + // If we only cared about being "technically" correct, that's an entirely legit + // implementation. + // + // Unfortunately, there is hardware out there that does not correctly implement the IEEE + // float semantics Rust relies on: x87 uses a too-large exponent, and some hardware flushes + // subnormals to zero. These are platforms bugs, and Rust will misbehave on such hardware, + // but we can at least try to make things seem as sane as possible by being careful here. // - // On some processors, in some cases, LLVM will "helpfully" lower floating point ops, - // in spite of a request for them using f32 and f64, to things like x87 operations. - // These have an f64's mantissa, but can have a larger than normal exponent. // FIXME(jubilee): Using x87 operations is never necessary in order to function // on x86 processors for Rust-to-Rust calls, so this issue should not happen. // Code generation should be adjusted to use non-C calling conventions, avoiding this. @@ -672,50 +674,30 @@ impl f64 { // 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: f64 to u64 is fine. Usually. - // If control flow has gotten this far, the value is definitely in one of the categories - // that f64::partial_classify can correctly analyze. - unsafe { f64::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. - #[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. - #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] - const fn classify_bits(b: u64) -> 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, + // So, this does use bitpattern matching for the rest. On x87, due to the incorrect + // float codegen on this hardware, this doesn't actually return a right answer for NaN + // because it cannot correctly discern between a floating point NaN, and some normal + // floating point numbers truncated from an x87 FPU -- but we took care of NaN above, so + // we are fine. + let b = self.to_bits(); + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { + (0, Self::EXP_MASK) => FpCategory::Infinite, + (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. + /// 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 or non-portable result. See the [specification + /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == 1.0` + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// let f = 7.0_f64; @@ -742,11 +724,14 @@ 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 or non-portable result. See the [specification + /// of NaN bit patterns](f32#nan-bit-patterns) for more info. Use `self.signum() == -1.0` + /// if you need fully portable behavior (will return `false` for all NaNs). /// /// ``` /// let f = 7.0_f64; @@ -801,6 +786,7 @@ impl f64 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX + #[inline] #[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 { @@ -849,6 +835,7 @@ impl f64 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX + #[inline] #[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 { @@ -983,7 +970,7 @@ impl f64 { /// Note that this follows the semantics specified in IEEE 754-2019. /// /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see [explanation of NaN as a special value](f32) for more info. + /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] @@ -1018,7 +1005,7 @@ impl f64 { /// Note that this follows the semantics specified in IEEE 754-2019. /// /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see [explanation of NaN as a special value](f32) for more info. + /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] @@ -1047,6 +1034,7 @@ impl f64 { /// assert_eq!(1f64.midpoint(4.0), 2.5); /// assert_eq!((-5.5f64).midpoint(8.0), 1.25); /// ``` + #[inline] #[unstable(feature = "num_midpoint", issue = "110840")] pub fn midpoint(self, other: f64) -> f64 { const LO: f64 = f64::MIN_POSITIVE * 2.; @@ -1060,13 +1048,13 @@ impl f64 { // Overflow is impossible (a + b) / 2. } else if abs_a < LO { - // Not safe to halve a + // Not safe to halve `a` (would underflow) a + (b / 2.) } else if abs_b < LO { - // Not safe to halve b + // Not safe to halve `b` (would underflow) (a / 2.) + b } else { - // Not safe to halve a and b + // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } @@ -1127,33 +1115,7 @@ impl f64 { #[inline] pub const fn to_bits(self) -> u64 { // SAFETY: `u64` is a plain old datatype so we can always transmute to it. - // ...sorta. - // - // See the SAFETY comment in f64::from_bits for more. - #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] - const fn ct_f64_to_u64(ct: f64) -> u64 { - match ct.classify() { - FpCategory::Nan => { - panic!("const-eval error: cannot use f64::to_bits on a NaN") - } - FpCategory::Subnormal => { - panic!("const-eval error: cannot use f64::to_bits on a subnormal number") - } - FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => { - // SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy. - unsafe { mem::transmute::(ct) } - } - } - } - - #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491 - fn rt_f64_to_u64(rt: f64) -> u64 { - // SAFETY: `u64` is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - unsafe { mem::transmute::(rt) } - } - intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) + unsafe { mem::transmute(self) } } /// Raw transmutation from `u64`. @@ -1198,61 +1160,11 @@ impl f64 { #[inline] pub const fn from_bits(v: u64) -> Self { // It turns out the safety issues with sNaN were overblown! Hooray! - // SAFETY: `u64` 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 an FTZ behavior by default: thumbv7neon - // aka "the Neon FPU in AArch32 state" - // - // Even with this, not all instructions exhibit the FTZ behaviors on thumbv7neon, - // so this should load the same bits if LLVM emits the "correct" instructions, - // but LLVM sometimes makes interesting choices about float optimization, - // and other FPUs may do similar. Thus, it is wise to indulge luxuriously in caution. - // - // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled, - // i.e. not soft-float, the way Rust does parameter passing can actually alter - // a number that is "not infinity" to have the same exponent as infinity, - // in a slightly unpredictable manner. - // - // 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. - #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")] - const fn ct_u64_to_f64(ct: u64) -> f64 { - match f64::classify_bits(ct) { - FpCategory::Subnormal => { - panic!("const-eval error: cannot use f64::from_bits on a subnormal number") - } - FpCategory::Nan => { - panic!("const-eval error: cannot use f64::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_u64_to_f64(rt: u64) -> f64 { - // SAFETY: `u64` is a plain old datatype so we can always... uh... - // ...look, just pretend you forgot what you just read. - // Stability concerns. - unsafe { mem::transmute::(rt) } - } - intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64) + // SAFETY: `u64` is a plain old datatype so we can always transmute from it. + unsafe { mem::transmute(v) } } - /// 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 @@ -1273,7 +1185,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 @@ -1294,7 +1206,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 @@ -1328,7 +1240,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). @@ -1347,7 +1259,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). @@ -1366,7 +1278,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 @@ -1396,7 +1308,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 d40e02352a1d0..878a911dde50d 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1223,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")] @@ -1311,6 +1312,34 @@ macro_rules! int_impl { } } + /// Unbounded shift left. Computes `self << rhs`, without bounding the value of `rhs` + /// + /// If `rhs` is larger or equal to the number of bits in `self`, + /// the entire value is shifted out, and `0` is returned. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(unbounded_shifts)] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(4), 0x10);")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(129), 0);")] + /// ``` + #[unstable(feature = "unbounded_shifts", issue = "129375")] + #[rustc_const_unstable(feature = "const_unbounded_shifts", issue = "129375")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn unbounded_shl(self, rhs: u32) -> $SelfT{ + if rhs < Self::BITS { + // SAFETY: + // rhs is just checked to be in-range above + unsafe { self.unchecked_shl(rhs) } + } else { + 0 + } + } + /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is /// larger than or equal to the number of bits in `self`. /// @@ -1409,6 +1438,40 @@ macro_rules! int_impl { } } + /// Unbounded shift right. Computes `self >> rhs`, without bounding the value of `rhs` + /// + /// If `rhs` is larger or equal to the number of bits in `self`, + /// the entire value is shifted out, which yields `0` for a positive number, + /// and `-1` for a negative number. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(unbounded_shifts)] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(4), 0x1);")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(129), 0);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.unbounded_shr(129), -1);")] + /// ``` + #[unstable(feature = "unbounded_shifts", issue = "129375")] + #[rustc_const_unstable(feature = "const_unbounded_shifts", issue = "129375")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn unbounded_shr(self, rhs: u32) -> $SelfT{ + if rhs < Self::BITS { + // SAFETY: + // rhs is just checked to be in-range above + unsafe { self.unchecked_shr(rhs) } + } else { + // A shift by `Self::BITS-1` suffices for signed integers, because the sign bit is copied for each of the shifted bits. + + // SAFETY: + // `Self::BITS-1` is guaranteed to be less than `Self::BITS` + unsafe { self.unchecked_shr(Self::BITS - 1) } + } + } + /// Checked absolute value. Computes `self.abs()`, returning `None` if /// `self == MIN`. /// @@ -1495,18 +1558,17 @@ macro_rules! int_impl { let mut base = self; let mut acc: Self = 1; - while exp > 1 { + loop { if (exp & 1) == 1 { acc = try_opt!(acc.checked_mul(base)); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return Some(acc); + } } exp /= 2; base = try_opt!(base.checked_mul(base)); } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.checked_mul(base) } /// Strict exponentiation. Computes `self.pow(exp)`, panicking if @@ -1546,18 +1608,17 @@ macro_rules! int_impl { let mut base = self; let mut acc: Self = 1; - while exp > 1 { + loop { if (exp & 1) == 1 { acc = acc.strict_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } } exp /= 2; base = base.strict_mul(base); } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.strict_mul(base) } /// Returns the square root of the number, rounded down. @@ -1580,7 +1641,33 @@ macro_rules! int_impl { if self < 0 { None } else { - Some((self as $UnsignedT).isqrt() as Self) + // SAFETY: Input is nonnegative in this `else` branch. + let result = unsafe { + crate::num::int_sqrt::$ActualT(self as $ActualT) as $SelfT + }; + + // Inform the optimizer what the range of outputs is. If + // testing `core` crashes with no panic message and a + // `num::int_sqrt::i*` test failed, it's because your edits + // caused these assertions to become false. + // + // SAFETY: Integer square root is a monotonically nondecreasing + // function, which means that increasing the input will never + // cause the output to decrease. Thus, since the input for + // nonnegative signed integers is bounded by + // `[0, <$ActualT>::MAX]`, sqrt(n) will be bounded by + // `[sqrt(0), sqrt(<$ActualT>::MAX)]`. + unsafe { + // SAFETY: `<$ActualT>::MAX` is nonnegative. + const MAX_RESULT: $SelfT = unsafe { + crate::num::int_sqrt::$ActualT(<$ActualT>::MAX) as $SelfT + }; + + crate::hint::assert_unchecked(result >= 0); + crate::hint::assert_unchecked(result <= MAX_RESULT); + } + + Some(result) } } @@ -2174,6 +2261,7 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] + #[rustc_allow_const_fn_unstable(is_val_statically_known)] pub const fn wrapping_pow(self, mut exp: u32) -> Self { if exp == 0 { return 1; @@ -2181,25 +2269,43 @@ macro_rules! int_impl { let mut base = self; let mut acc: Self = 1; - while exp > 1 { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); + if intrinsics::is_val_statically_known(exp) { + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); + } + exp /= 2; + base = base.wrapping_mul(base); } - exp /= 2; - base = base.wrapping_mul(base); - } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.wrapping_mul(base) + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary. + acc.wrapping_mul(base) + } else { + // This is faster than the above when the exponent is not known + // at compile time. We can't use the same code for the constant + // exponent case because LLVM is currently unable to unroll + // this loop. + loop { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } + } + exp /= 2; + base = base.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 /// @@ -2277,7 +2383,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 @@ -2600,6 +2706,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")] @@ -2687,9 +2794,14 @@ macro_rules! int_impl { // Scratch space for storing results of overflowing_mul. let mut r; - while exp > 1 { + loop { if (exp & 1) == 1 { r = acc.overflowing_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + r.1 |= overflown; + return r; + } acc = r.0; overflown |= r.1; } @@ -2698,14 +2810,6 @@ macro_rules! int_impl { base = r.0; overflown |= r.1; } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - r = acc.overflowing_mul(base); - r.1 |= overflown; - r } /// Raises self to the power of `exp`, using exponentiation by squaring. @@ -2725,6 +2829,7 @@ macro_rules! int_impl { without modifying the original"] #[inline] #[rustc_inherit_overflow_checks] + #[rustc_allow_const_fn_unstable(is_val_statically_known)] pub const fn pow(self, mut exp: u32) -> Self { if exp == 0 { return 1; @@ -2732,19 +2837,37 @@ macro_rules! int_impl { let mut base = self; let mut acc = 1; - while exp > 1 { - if (exp & 1) == 1 { - acc = acc * base; + if intrinsics::is_val_statically_known(exp) { + while exp > 1 { + if (exp & 1) == 1 { + acc = acc * base; + } + exp /= 2; + base = base * base; } - exp /= 2; - base = base * base; - } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc * base + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc * base + } else { + // This is faster than the above when the exponent is not known + // at compile time. We can't use the same code for the constant + // exponent case because LLVM is currently unable to unroll + // this loop. + loop { + if (exp & 1) == 1 { + acc = acc * base; + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } + } + exp /= 2; + base = base * base; + } + } } /// Returns the square root of the number, rounded down. @@ -2765,15 +2888,11 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] + #[track_caller] pub const fn isqrt(self) -> Self { - // I would like to implement it as - // ``` - // self.checked_isqrt().expect("argument of integer square root must be non-negative") - // ``` - // but `expect` is not yet stable as a `const fn`. match self.checked_isqrt() { Some(sqrt) => sqrt, - None => panic!("argument of integer square root must be non-negative"), + None => crate::num::int_sqrt::panic_for_negative_argument(), } } @@ -2791,8 +2910,8 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0 or if `self` is -1 and `rhs` is - /// `Self::MIN`. This behavior is not affected by the `overflow-checks` flag. + /// This function will panic if `rhs` is 0 or if `self` is `Self::MIN` + /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples /// @@ -2830,8 +2949,8 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0 or if `self` is -1 and `rhs` is - /// `Self::MIN`. This behavior is not affected by the `overflow-checks` flag. + /// This function will panic if `rhs` is 0 or if `self` is `Self::MIN` and + /// `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples /// @@ -2846,6 +2965,11 @@ macro_rules! int_impl { /// assert_eq!(a.rem_euclid(-b), 3); /// assert_eq!((-a).rem_euclid(-b), 1); /// ``` + /// + /// This will panic: + /// ```should_panic + #[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.rem_euclid(-1);")] + /// ``` #[doc(alias = "modulo", alias = "mod")] #[stable(feature = "euclidean_division", since = "1.38.0")] #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] @@ -2874,8 +2998,8 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0 or if `self` is -1 and `rhs` is - /// `Self::MIN`. This behavior is not affected by the `overflow-checks` flag. + /// This function will panic if `rhs` is 0 or if `self` is `Self::MIN` + /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples /// @@ -2910,8 +3034,8 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0 or if `self` is -1 and `rhs` is - /// `Self::MIN`. This behavior is not affected by the `overflow-checks` flag. + /// This function will panic if `rhs` is 0 or if `self` is `Self::MIN` + /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples /// @@ -3389,7 +3513,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] @@ -3409,7 +3533,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] @@ -3429,7 +3553,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 @@ -3467,7 +3591,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] @@ -3496,7 +3620,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] @@ -3525,7 +3649,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/int_sqrt.rs b/library/core/src/num/int_sqrt.rs new file mode 100644 index 0000000000000..601e81f69930f --- /dev/null +++ b/library/core/src/num/int_sqrt.rs @@ -0,0 +1,316 @@ +//! These functions use the [Karatsuba square root algorithm][1] to compute the +//! [integer square root](https://en.wikipedia.org/wiki/Integer_square_root) +//! for the primitive integer types. +//! +//! The signed integer functions can only handle **nonnegative** inputs, so +//! that must be checked before calling those. +//! +//! [1]: +//! "Paul Zimmermann. Karatsuba Square Root. \[Research Report\] RR-3805, +//! INRIA. 1999, pp.8. (inria-00072854)" + +/// This array stores the [integer square roots]( +/// https://en.wikipedia.org/wiki/Integer_square_root) and remainders of each +/// [`u8`](prim@u8) value. For example, `U8_ISQRT_WITH_REMAINDER[17]` will be +/// `(4, 1)` because the integer square root of 17 is 4 and because 17 is 1 +/// higher than 4 squared. +const U8_ISQRT_WITH_REMAINDER: [(u8, u8); 256] = { + let mut result = [(0, 0); 256]; + + let mut n: usize = 0; + let mut isqrt_n: usize = 0; + while n < result.len() { + result[n] = (isqrt_n as u8, (n - isqrt_n.pow(2)) as u8); + + n += 1; + if n == (isqrt_n + 1).pow(2) { + isqrt_n += 1; + } + } + + result +}; + +/// Returns the [integer square root]( +/// https://en.wikipedia.org/wiki/Integer_square_root) of any [`u8`](prim@u8) +/// input. +#[must_use = "this returns the result of the operation, \ + without modifying the original"] +#[inline] +pub const fn u8(n: u8) -> u8 { + U8_ISQRT_WITH_REMAINDER[n as usize].0 +} + +/// Generates an `i*` function that returns the [integer square root]( +/// https://en.wikipedia.org/wiki/Integer_square_root) of any **nonnegative** +/// input of a specific signed integer type. +macro_rules! signed_fn { + ($SignedT:ident, $UnsignedT:ident) => { + /// Returns the [integer square root]( + /// https://en.wikipedia.org/wiki/Integer_square_root) of any + /// **nonnegative** + #[doc = concat!("[`", stringify!($SignedT), "`](prim@", stringify!($SignedT), ")")] + /// input. + /// + /// # Safety + /// + /// This results in undefined behavior when the input is negative. + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const unsafe fn $SignedT(n: $SignedT) -> $SignedT { + debug_assert!(n >= 0, "Negative input inside `isqrt`."); + $UnsignedT(n as $UnsignedT) as $SignedT + } + }; +} + +signed_fn!(i8, u8); +signed_fn!(i16, u16); +signed_fn!(i32, u32); +signed_fn!(i64, u64); +signed_fn!(i128, u128); + +/// Generates a `u*` function that returns the [integer square root]( +/// https://en.wikipedia.org/wiki/Integer_square_root) of any input of +/// a specific unsigned integer type. +macro_rules! unsigned_fn { + ($UnsignedT:ident, $HalfBitsT:ident, $stages:ident) => { + /// Returns the [integer square root]( + /// https://en.wikipedia.org/wiki/Integer_square_root) of any + #[doc = concat!("[`", stringify!($UnsignedT), "`](prim@", stringify!($UnsignedT), ")")] + /// input. + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn $UnsignedT(mut n: $UnsignedT) -> $UnsignedT { + if n <= <$HalfBitsT>::MAX as $UnsignedT { + $HalfBitsT(n as $HalfBitsT) as $UnsignedT + } else { + // The normalization shift satisfies the Karatsuba square root + // algorithm precondition "a₃ ≥ b/4" where a₃ is the most + // significant quarter of `n`'s bits and b is the number of + // values that can be represented by that quarter of the bits. + // + // b/4 would then be all 0s except the second most significant + // bit (010...0) in binary. Since a₃ must be at least b/4, a₃'s + // most significant bit or its neighbor must be a 1. Since a₃'s + // most significant bits are `n`'s most significant bits, the + // same applies to `n`. + // + // The reason to shift by an even number of bits is because an + // even number of bits produces the square root shifted to the + // left by half of the normalization shift: + // + // sqrt(n << (2 * p)) + // sqrt(2.pow(2 * p) * n) + // sqrt(2.pow(2 * p)) * sqrt(n) + // 2.pow(p) * sqrt(n) + // sqrt(n) << p + // + // Shifting by an odd number of bits leaves an ugly sqrt(2) + // multiplied in: + // + // sqrt(n << (2 * p + 1)) + // sqrt(2.pow(2 * p + 1) * n) + // sqrt(2 * 2.pow(2 * p) * n) + // sqrt(2) * sqrt(2.pow(2 * p)) * sqrt(n) + // sqrt(2) * 2.pow(p) * sqrt(n) + // sqrt(2) * (sqrt(n) << p) + const EVEN_MAKING_BITMASK: u32 = !1; + let normalization_shift = n.leading_zeros() & EVEN_MAKING_BITMASK; + n <<= normalization_shift; + + let s = $stages(n); + + let denormalization_shift = normalization_shift >> 1; + s >> denormalization_shift + } + } + }; +} + +/// Generates the first stage of the computation after normalization. +/// +/// # Safety +/// +/// `$n` must be nonzero. +macro_rules! first_stage { + ($original_bits:literal, $n:ident) => {{ + debug_assert!($n != 0, "`$n` is zero in `first_stage!`."); + + const N_SHIFT: u32 = $original_bits - 8; + let n = $n >> N_SHIFT; + + let (s, r) = U8_ISQRT_WITH_REMAINDER[n as usize]; + + // Inform the optimizer that `s` is nonzero. This will allow it to + // avoid generating code to handle division-by-zero panics in the next + // stage. + // + // SAFETY: If the original `$n` is zero, the top of the `unsigned_fn` + // macro recurses instead of continuing to this point, so the original + // `$n` wasn't a 0 if we've reached here. + // + // Then the `unsigned_fn` macro normalizes `$n` so that at least one of + // its two most-significant bits is a 1. + // + // Then this stage puts the eight most-significant bits of `$n` into + // `n`. This means that `n` here has at least one 1 bit in its two + // most-significant bits, making `n` nonzero. + // + // `U8_ISQRT_WITH_REMAINDER[n as usize]` will give a nonzero `s` when + // given a nonzero `n`. + unsafe { crate::hint::assert_unchecked(s != 0) }; + (s, r) + }}; +} + +/// Generates a middle stage of the computation. +/// +/// # Safety +/// +/// `$s` must be nonzero. +macro_rules! middle_stage { + ($original_bits:literal, $ty:ty, $n:ident, $s:ident, $r:ident) => {{ + debug_assert!($s != 0, "`$s` is zero in `middle_stage!`."); + + const N_SHIFT: u32 = $original_bits - <$ty>::BITS; + let n = ($n >> N_SHIFT) as $ty; + + const HALF_BITS: u32 = <$ty>::BITS >> 1; + const QUARTER_BITS: u32 = <$ty>::BITS >> 2; + const LOWER_HALF_1_BITS: $ty = (1 << HALF_BITS) - 1; + const LOWEST_QUARTER_1_BITS: $ty = (1 << QUARTER_BITS) - 1; + + let lo = n & LOWER_HALF_1_BITS; + let numerator = (($r as $ty) << QUARTER_BITS) | (lo >> QUARTER_BITS); + let denominator = ($s as $ty) << 1; + let q = numerator / denominator; + let u = numerator % denominator; + + let mut s = ($s << QUARTER_BITS) as $ty + q; + let (mut r, overflow) = + ((u << QUARTER_BITS) | (lo & LOWEST_QUARTER_1_BITS)).overflowing_sub(q * q); + if overflow { + r = r.wrapping_add(2 * s - 1); + s -= 1; + } + + // Inform the optimizer that `s` is nonzero. This will allow it to + // avoid generating code to handle division-by-zero panics in the next + // stage. + // + // SAFETY: If the original `$n` is zero, the top of the `unsigned_fn` + // macro recurses instead of continuing to this point, so the original + // `$n` wasn't a 0 if we've reached here. + // + // Then the `unsigned_fn` macro normalizes `$n` so that at least one of + // its two most-significant bits is a 1. + // + // Then these stages take as many of the most-significant bits of `$n` + // as will fit in this stage's type. For example, the stage that + // handles `u32` deals with the 32 most-significant bits of `$n`. This + // means that each stage has at least one 1 bit in `n`'s two + // most-significant bits, making `n` nonzero. + // + // Then this stage will produce the correct integer square root for + // that `n` value. Since `n` is nonzero, `s` will also be nonzero. + unsafe { crate::hint::assert_unchecked(s != 0) }; + (s, r) + }}; +} + +/// Generates the last stage of the computation before denormalization. +/// +/// # Safety +/// +/// `$s` must be nonzero. +macro_rules! last_stage { + ($ty:ty, $n:ident, $s:ident, $r:ident) => {{ + debug_assert!($s != 0, "`$s` is zero in `last_stage!`."); + + const HALF_BITS: u32 = <$ty>::BITS >> 1; + const QUARTER_BITS: u32 = <$ty>::BITS >> 2; + const LOWER_HALF_1_BITS: $ty = (1 << HALF_BITS) - 1; + + let lo = $n & LOWER_HALF_1_BITS; + let numerator = (($r as $ty) << QUARTER_BITS) | (lo >> QUARTER_BITS); + let denominator = ($s as $ty) << 1; + + let q = numerator / denominator; + let mut s = ($s << QUARTER_BITS) as $ty + q; + let (s_squared, overflow) = s.overflowing_mul(s); + if overflow || s_squared > $n { + s -= 1; + } + s + }}; +} + +/// Takes the normalized [`u16`](prim@u16) input and gets its normalized +/// [integer square root](https://en.wikipedia.org/wiki/Integer_square_root). +/// +/// # Safety +/// +/// `n` must be nonzero. +#[inline] +const fn u16_stages(n: u16) -> u16 { + let (s, r) = first_stage!(16, n); + last_stage!(u16, n, s, r) +} + +/// Takes the normalized [`u32`](prim@u32) input and gets its normalized +/// [integer square root](https://en.wikipedia.org/wiki/Integer_square_root). +/// +/// # Safety +/// +/// `n` must be nonzero. +#[inline] +const fn u32_stages(n: u32) -> u32 { + let (s, r) = first_stage!(32, n); + let (s, r) = middle_stage!(32, u16, n, s, r); + last_stage!(u32, n, s, r) +} + +/// Takes the normalized [`u64`](prim@u64) input and gets its normalized +/// [integer square root](https://en.wikipedia.org/wiki/Integer_square_root). +/// +/// # Safety +/// +/// `n` must be nonzero. +#[inline] +const fn u64_stages(n: u64) -> u64 { + let (s, r) = first_stage!(64, n); + let (s, r) = middle_stage!(64, u16, n, s, r); + let (s, r) = middle_stage!(64, u32, n, s, r); + last_stage!(u64, n, s, r) +} + +/// Takes the normalized [`u128`](prim@u128) input and gets its normalized +/// [integer square root](https://en.wikipedia.org/wiki/Integer_square_root). +/// +/// # Safety +/// +/// `n` must be nonzero. +#[inline] +const fn u128_stages(n: u128) -> u128 { + let (s, r) = first_stage!(128, n); + let (s, r) = middle_stage!(128, u16, n, s, r); + let (s, r) = middle_stage!(128, u32, n, s, r); + let (s, r) = middle_stage!(128, u64, n, s, r); + last_stage!(u128, n, s, r) +} + +unsigned_fn!(u16, u8, u16_stages); +unsigned_fn!(u32, u16, u32_stages); +unsigned_fn!(u64, u32, u64_stages); +unsigned_fn!(u128, u64, u128_stages); + +/// Instantiate this panic logic once, rather than for all the isqrt methods +/// on every single primitive type. +#[cold] +#[track_caller] +pub const fn panic_for_negative_argument() -> ! { + panic!("argument of integer square root cannot be negative") +} diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 034af6a0d5731..37c9db7f474b5 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -2,12 +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 { @@ -44,44 +41,37 @@ mod uint_macros; // import uint_impl! mod error; mod int_log10; +mod int_sqrt; mod nonzero; 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 { () => { @@ -484,6 +474,7 @@ impl u8 { ActualT = u8, SignedT = i8, BITS = 8, + BITS_MINUS_ONE = 7, MAX = 255, rot = 2, rot_op = "0x82", @@ -1098,6 +1089,7 @@ impl u16 { ActualT = u16, SignedT = i16, BITS = 16, + BITS_MINUS_ONE = 15, MAX = 65535, rot = 4, rot_op = "0xa003", @@ -1146,6 +1138,7 @@ impl u32 { ActualT = u32, SignedT = i32, BITS = 32, + BITS_MINUS_ONE = 31, MAX = 4294967295, rot = 8, rot_op = "0x10000b3", @@ -1169,6 +1162,7 @@ impl u64 { ActualT = u64, SignedT = i64, BITS = 64, + BITS_MINUS_ONE = 63, MAX = 18446744073709551615, rot = 12, rot_op = "0xaa00000000006e1", @@ -1192,6 +1186,7 @@ impl u128 { ActualT = u128, SignedT = i128, BITS = 128, + BITS_MINUS_ONE = 127, MAX = 340282366920938463463374607431768211455, rot = 16, rot_op = "0x13f40000000000000000000000004f76", @@ -1217,6 +1212,7 @@ impl usize { ActualT = u16, SignedT = isize, BITS = 16, + BITS_MINUS_ONE = 15, MAX = 65535, rot = 4, rot_op = "0xa003", @@ -1241,6 +1237,7 @@ impl usize { ActualT = u32, SignedT = isize, BITS = 32, + BITS_MINUS_ONE = 31, MAX = 4294967295, rot = 8, rot_op = "0x10000b3", @@ -1265,6 +1262,7 @@ impl usize { ActualT = u64, SignedT = isize, BITS = 64, + BITS_MINUS_ONE = 63, MAX = 18446744073709551615, rot = 12, rot_op = "0xaa00000000006e1", @@ -1387,6 +1385,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 = "1.82.0")] 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 } @@ -1436,7 +1435,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 = "1.82.0")] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { use self::IntErrorKind::*; use self::ParseIntError as PIE; @@ -1566,7 +1565,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 = "1.82.0")] 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 0c6f06dc017e7..8b888f12da0b1 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, intrinsics, ptr, ub_checks}; /// A marker trait for primitive types which can be zero. /// @@ -454,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. @@ -603,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, } @@ -823,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)), } }; @@ -849,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; @@ -897,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; @@ -918,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, ) => { @@ -1224,11 +1520,44 @@ 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 { + let result = self.get().isqrt(); + + // SAFETY: Integer square root is a monotonically nondecreasing + // function, which means that increasing the input will never cause + // the output to decrease. Thus, since the input for nonzero + // unsigned integers has a lower bound of 1, the lower bound of the + // results will be sqrt(1), which is 1, so a result can't be zero. + unsafe { Self::new_unchecked(result) } + } }; // Associated items for signed nonzero types only. ( - Self = $Ty:ident, Primitive = signed $Int:ident, UnsignedPrimitive = $Uint:ty, ) => { @@ -1656,65 +1985,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 ad72c29758bd7..d9036abecc592 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -9,6 +9,7 @@ macro_rules! uint_impl { // 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, @@ -65,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")] @@ -86,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")] @@ -108,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")] @@ -130,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")] @@ -150,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")] @@ -171,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")] @@ -736,6 +766,67 @@ macro_rules! uint_impl { } } + #[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 /// `None` if overflow occurred. /// @@ -859,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 /// @@ -918,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 @@ -981,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 /// @@ -1041,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 /// @@ -1226,10 +1315,9 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn checked_ilog2(self) -> Option { - if let Some(x) = NonZero::new(self) { - Some(x.ilog2()) - } else { - None + match NonZero::new(self) { + Some(x) => Some(x.ilog2()), + None => None, } } @@ -1248,10 +1336,9 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn checked_ilog10(self) -> Option { - if let Some(x) = NonZero::new(self) { - Some(x.ilog10()) - } else { - None + match NonZero::new(self) { + Some(x) => Some(x.ilog10()), + None => None, } } @@ -1325,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")] @@ -1413,6 +1501,34 @@ macro_rules! uint_impl { } } + /// Unbounded shift left. Computes `self << rhs`, without bounding the value of `rhs` + /// + /// If `rhs` is larger or equal to the number of bits in `self`, + /// the entire value is shifted out, and `0` is returned. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(unbounded_shifts)] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(4), 0x10);")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(129), 0);")] + /// ``` + #[unstable(feature = "unbounded_shifts", issue = "129375")] + #[rustc_const_unstable(feature = "const_unbounded_shifts", issue = "129375")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn unbounded_shl(self, rhs: u32) -> $SelfT{ + if rhs < Self::BITS { + // SAFETY: + // rhs is just checked to be in-range above + unsafe { self.unchecked_shl(rhs) } + } else { + 0 + } + } + /// Checked shift right. Computes `self >> rhs`, returning `None` /// if `rhs` is larger than or equal to the number of bits in `self`. /// @@ -1511,6 +1627,34 @@ macro_rules! uint_impl { } } + /// Unbounded shift right. Computes `self >> rhs`, without bounding the value of `rhs` + /// + /// If `rhs` is larger or equal to the number of bits in `self`, + /// the entire value is shifted out, and `0` is returned. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(unbounded_shifts)] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(4), 0x1);")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(129), 0);")] + /// ``` + #[unstable(feature = "unbounded_shifts", issue = "129375")] + #[rustc_const_unstable(feature = "const_unbounded_shifts", issue = "129375")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn unbounded_shr(self, rhs: u32) -> $SelfT{ + if rhs < Self::BITS { + // SAFETY: + // rhs is just checked to be in-range above + unsafe { self.unchecked_shr(rhs) } + } else { + 0 + } + } + /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if /// overflow occurred. /// @@ -1534,20 +1678,17 @@ macro_rules! uint_impl { let mut base = self; let mut acc: Self = 1; - while exp > 1 { + loop { if (exp & 1) == 1 { acc = try_opt!(acc.checked_mul(base)); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return Some(acc); + } } exp /= 2; base = try_opt!(base.checked_mul(base)); } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - - acc.checked_mul(base) } /// Strict exponentiation. Computes `self.pow(exp)`, panicking if @@ -1587,18 +1728,17 @@ macro_rules! uint_impl { let mut base = self; let mut acc: Self = 1; - while exp > 1 { + loop { if (exp & 1) == 1 { acc = acc.strict_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } } exp /= 2; base = base.strict_mul(base); } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.strict_mul(base) } /// Saturating integer addition. Computes `self + rhs`, saturating at @@ -1826,10 +1966,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 /// @@ -1853,13 +1993,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 /// @@ -1883,11 +2022,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 /// @@ -1911,14 +2050,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 /// @@ -2052,6 +2190,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] + #[rustc_allow_const_fn_unstable(is_val_statically_known)] pub const fn wrapping_pow(self, mut exp: u32) -> Self { if exp == 0 { return 1; @@ -2059,22 +2198,39 @@ macro_rules! uint_impl { let mut base = self; let mut acc: Self = 1; - while exp > 1 { - if (exp & 1) == 1 { - acc = acc.wrapping_mul(base); + if intrinsics::is_val_statically_known(exp) { + while exp > 1 { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); + } + exp /= 2; + base = base.wrapping_mul(base); } - exp /= 2; - base = base.wrapping_mul(base); - } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc.wrapping_mul(base) + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary. + acc.wrapping_mul(base) + } else { + // This is faster than the above when the exponent is not known + // at compile time. We can't use the same code for the constant + // exponent case because LLVM is currently unable to unroll + // this loop. + loop { + if (exp & 1) == 1 { + acc = acc.wrapping_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } + } + exp /= 2; + base = base.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 @@ -2148,7 +2304,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 @@ -2453,6 +2609,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")] @@ -2516,9 +2673,14 @@ macro_rules! uint_impl { // Scratch space for storing results of overflowing_mul. let mut r; - while exp > 1 { + loop { if (exp & 1) == 1 { r = acc.overflowing_mul(base); + // since exp!=0, finally the exp must be 1. + if exp == 1 { + r.1 |= overflown; + return r; + } acc = r.0; overflown |= r.1; } @@ -2527,15 +2689,6 @@ macro_rules! uint_impl { base = r.0; overflown |= r.1; } - - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - r = acc.overflowing_mul(base); - r.1 |= overflown; - - r } /// Raises self to the power of `exp`, using exponentiation by squaring. @@ -2553,6 +2706,7 @@ macro_rules! uint_impl { without modifying the original"] #[inline] #[rustc_inherit_overflow_checks] + #[rustc_allow_const_fn_unstable(is_val_statically_known)] pub const fn pow(self, mut exp: u32) -> Self { if exp == 0 { return 1; @@ -2560,19 +2714,37 @@ macro_rules! uint_impl { let mut base = self; let mut acc = 1; - while exp > 1 { - if (exp & 1) == 1 { - acc = acc * base; + if intrinsics::is_val_statically_known(exp) { + while exp > 1 { + if (exp & 1) == 1 { + acc = acc * base; + } + exp /= 2; + base = base * base; } - exp /= 2; - base = base * base; - } - // since exp!=0, finally the exp must be 1. - // Deal with the final bit of the exponent separately, since - // squaring the base afterwards is not necessary and may cause a - // needless overflow. - acc * base + // since exp!=0, finally the exp must be 1. + // Deal with the final bit of the exponent separately, since + // squaring the base afterwards is not necessary and may cause a + // needless overflow. + acc * base + } else { + // This is faster than the above when the exponent is not known + // at compile time. We can't use the same code for the constant + // exponent case because LLVM is currently unable to unroll + // this loop. + loop { + if (exp & 1) == 1 { + acc = acc * base; + // since exp!=0, finally the exp must be 1. + if exp == 1 { + return acc; + } + } + exp /= 2; + base = base * base; + } + } } /// Returns the square root of the number, rounded down. @@ -2590,37 +2762,24 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn isqrt(self) -> Self { - if self < 2 { - return self; - } - - // 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. + let result = crate::num::int_sqrt::$ActualT(self as $ActualT) as $SelfT; + + // Inform the optimizer what the range of outputs is. If testing + // `core` crashes with no panic message and a `num::int_sqrt::u*` + // test failed, it's because your edits caused these assertions or + // the assertions in `fn isqrt` of `nonzero.rs` to become false. + // + // SAFETY: Integer square root is a monotonically nondecreasing + // function, which means that increasing the input will never + // cause the output to decrease. Thus, since the input for unsigned + // integers is bounded by `[0, <$ActualT>::MAX]`, sqrt(n) will be + // bounded by `[sqrt(0), sqrt(<$ActualT>::MAX)]`. unsafe { - hint::assert_unchecked(0 < res); - hint::assert_unchecked(res < 1 << (Self::BITS / 2)); + const MAX_RESULT: $SelfT = crate::num::int_sqrt::$ActualT(<$ActualT>::MAX) as $SelfT; + crate::hint::assert_unchecked(result <= MAX_RESULT); } - res + result } /// Performs Euclidean division. @@ -2793,6 +2952,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 @@ -2906,7 +3094,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] @@ -2926,7 +3114,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] @@ -2946,7 +3134,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 @@ -2984,7 +3172,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] @@ -3013,7 +3201,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] @@ -3042,7 +3230,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/async_function.rs b/library/core/src/ops/async_function.rs index 48d1042d9df4a..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"] @@ -39,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"] diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index e10c438ef4300..ab73dc19fcc73 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -116,7 +116,9 @@ impl ops::Try for ControlFlow { } #[unstable(feature = "try_trait_v2", issue = "84277")] -impl ops::FromResidual for ControlFlow { +// Note: manually specifying the residual type instead of using the default to work around +// https://github.com/rust-lang/rust/issues/99940 +impl ops::FromResidual> for ControlFlow { #[inline] fn from_residual(residual: ControlFlow) -> Self { match residual { @@ -238,7 +240,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 +249,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 753f14c6b85ec..c7d596d74c383 100644 --- a/library/core/src/ops/coroutine.rs +++ b/library/core/src/ops/coroutine.rs @@ -69,6 +69,7 @@ pub enum CoroutineState { #[lang = "coroutine"] #[unstable(feature = "coroutine_trait", issue = "43122")] #[fundamental] +#[must_use = "coroutines are lazy and do nothing unless resumed"] pub trait Coroutine { /// The type of value this coroutine yields. /// @@ -76,7 +77,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. - #[cfg_attr(not(bootstrap), lang = "coroutine_yield")] + #[lang = "coroutine_yield"] type Yield; /// The type of value this coroutine returns. @@ -85,7 +86,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. - #[cfg_attr(not(bootstrap), lang = "coroutine_return")] + #[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/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/option.rs b/library/core/src/option.rs index 8ec7716012f59..212e4f0215463 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -557,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)] @@ -659,8 +656,6 @@ impl Option { /// # Examples /// /// ``` - /// #![feature(is_none_or)] - /// /// let x: Option = Some(2); /// assert_eq!(x.is_none_or(|x| x > 1), true); /// @@ -672,7 +667,7 @@ impl Option { /// ``` #[must_use] #[inline] - #[unstable(feature = "is_none_or", issue = "126383")] + #[stable(feature = "is_none_or", since = "1.82.0")] pub fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool { match self { None => true, @@ -770,6 +765,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. @@ -812,7 +814,7 @@ impl Option { unsafe { slice::from_raw_parts( (self as *const Self).byte_add(core::mem::offset_of!(Self, Some.0)).cast(), - self.is_some() as usize, + self.len(), ) } } @@ -869,7 +871,7 @@ impl Option { unsafe { slice::from_raw_parts_mut( (self as *mut Self).byte_add(core::mem::offset_of!(Self, Some.0)).cast(), - self.is_some() as usize, + self.len(), ) } } @@ -2242,10 +2244,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)) } } @@ -2256,7 +2256,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 {} @@ -2488,7 +2493,9 @@ impl ops::Try for Option { } #[unstable(feature = "try_trait_v2", issue = "84277")] -impl ops::FromResidual for Option { +// Note: manually specifying the residual type instead of using the default to work around +// https://github.com/rust-lang/rust/issues/99940 +impl ops::FromResidual> for Option { #[inline] fn from_residual(residual: Option) -> Self { match residual { @@ -2497,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 37c338dd9b778..6c5236ed99ce8 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -6,16 +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 = "CURRENT_RUSTC_VERSION")] +#[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")] @@ -160,7 +159,7 @@ pub unsafe trait PanicPayload: crate::fmt::Display { /// Just borrow the contents. fn get(&mut self) -> &(dyn Any + Send); - /// Try to borrow the contents as `&str`, if possible without doing any allocations. + /// 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 8c04994ac0fc4..e2a842046a96d 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -44,7 +44,7 @@ impl<'a> Location<'a> { /// /// # Examples /// - /// ``` + /// ```standalone /// use std::panic::Location; /// /// /// Returns the [`Location`] at which it is called. @@ -195,6 +195,7 @@ impl<'a> Location<'a> { #[stable(feature = "panic_hook_display", since = "1.26.0")] impl fmt::Display for Location<'_> { + #[inline] fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { write!(formatter, "{}:{}:{}", self.file, self.line, self.col) } diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index 6bbb9c3017110..e4d0c897b65ca 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -24,7 +24,7 @@ pub struct PanicInfo<'a> { /// that were given to the `panic!()` macro. /// /// See [`PanicInfo::message`]. -#[stable(feature = "panic_info_message", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "panic_info_message", since = "1.81.0")] pub struct PanicMessage<'a> { message: fmt::Arguments<'a>, } @@ -57,7 +57,7 @@ impl<'a> PanicInfo<'a> { /// } /// ``` #[must_use] - #[stable(feature = "panic_info_message", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "panic_info_message", since = "1.81.0")] pub fn message(&self) -> PanicMessage<'_> { PanicMessage { message: self.message } } @@ -152,7 +152,7 @@ impl Display for PanicInfo<'_> { } impl<'a> PanicMessage<'a> { - /// Get the formatted message, if it has no arguments to be formatted at runtime. + /// Gets the formatted message, if it has no arguments to be formatted at runtime. /// /// This can be used to avoid allocations in some cases. /// @@ -164,7 +164,7 @@ impl<'a> PanicMessage<'a> { /// For most cases with placeholders, this function will return `None`. /// /// See [`fmt::Arguments::as_str`] for details. - #[stable(feature = "panic_info_message", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "panic_info_message", since = "1.81.0")] #[rustc_const_unstable(feature = "const_arguments_as_str", issue = "103900")] #[must_use] #[inline] @@ -173,7 +173,7 @@ impl<'a> PanicMessage<'a> { } } -#[stable(feature = "panic_info_message", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "panic_info_message", since = "1.81.0")] impl Display for PanicMessage<'_> { #[inline] fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -181,7 +181,7 @@ impl Display for PanicMessage<'_> { } } -#[stable(feature = "panic_info_message", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "panic_info_message", since = "1.81.0")] impl fmt::Debug for PanicMessage<'_> { #[inline] fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 97fb1d6b7323f..e4a623040871a 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -264,7 +264,7 @@ pub const fn panic_display(x: &T) -> ! { panic_fmt(format_args!("{}", *x)); } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] #[lang = "panic_bounds_check"] // needed by codegen for panic on OOB array/slice access @@ -276,7 +276,7 @@ fn panic_bounds_check(index: usize, len: usize) -> ! { panic!("index out of bounds: the len is {len} but the index is {index}") } -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] #[lang = "panic_misaligned_pointer_dereference"] // needed by codegen for panic on misaligned pointer deref @@ -294,13 +294,14 @@ 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)] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[lang = "panic_cannot_unwind"] // needed by codegen for panic in nounwind function #[rustc_nounwind] @@ -309,13 +310,14 @@ 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)] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[lang = "panic_in_cleanup"] // needed by codegen for panic in nounwind function #[rustc_nounwind] @@ -348,7 +350,7 @@ pub enum AssertKind { } /// Internal function for `assert_eq!` and `assert_ne!` macros -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] #[doc(hidden)] @@ -366,7 +368,7 @@ where } /// Internal function for `assert_match!` -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] #[doc(hidden)] @@ -386,7 +388,7 @@ pub fn assert_matches_failed( } /// Non-generic version of the above functions, to avoid code bloat. -#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] fn assert_failed_inner( diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs index a10c45933428d..1f89d960be67b 100644 --- a/library/core/src/pat.rs +++ b/library/core/src/pat.rs @@ -6,7 +6,7 @@ /// ``` #[macro_export] #[rustc_builtin_macro(pattern_type)] -#[unstable(feature = "core_pattern_type", issue = "none")] +#[unstable(feature = "core_pattern_type", issue = "123646")] macro_rules! pattern_type { ($($arg:tt)*) => { /* compiler built-in */ diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 0d2aa3070a19f..9c13662e08e8f 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -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. /// @@ -1086,6 +1084,7 @@ use crate::{ #[lang = "pin"] #[fundamental] #[repr(transparent)] +#[rustc_pub_transparent] #[derive(Copy, Clone)] pub struct Pin { // FIXME(#93176): this field is made `#[unstable] #[doc(hidden)] pub` to: @@ -1168,7 +1167,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 +1222,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 @@ -1293,8 +1292,8 @@ impl Pin { /// // Now, if `x` was the only reference, we have a mutable reference to /// // data that we pinned above, which we could use to move it as we have /// // seen in the previous example. We have violated the pinning API contract. - /// } - /// ``` + /// } + /// ``` /// /// ## Pinning of closure captures /// @@ -1371,33 +1370,14 @@ impl Pin { // SAFETY: see documentation on this function unsafe { Pin::new_unchecked(&*self.__pointer) } } - - /// Unwraps this `Pin`, returning the underlying `Ptr`. - /// - /// # Safety - /// - /// This function is unsafe. You must guarantee that you will continue to - /// treat the pointer `Ptr` as pinned after you call this function, so that - /// the invariants on the `Pin` type can be upheld. If the code using the - /// resulting `Ptr` does not continue to maintain the pinning invariants that - /// is a violation of the API contract and may lead to undefined behavior in - /// later (safe) operations. - /// - /// Note that you must be able to guarantee that the data pointed to by `Ptr` - /// will be treated as pinned all the way until its `drop` handler is complete! - /// - /// *For more information, see the [`pin` module docs][self]* - /// - /// If the underlying data is [`Unpin`], [`Pin::into_inner`] should be used - /// instead. - #[inline(always)] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] - #[stable(feature = "pin_into_inner", since = "1.39.0")] - pub const unsafe fn into_inner_unchecked(pin: Pin) -> Ptr { - pin.__pointer - } } +// These methods being in a `Ptr: DerefMut` impl block concerns semver stability. +// Currently, calling e.g. `.set()` on a `Pin<&T>` sees that `Ptr: DerefMut` +// doesn't hold, and goes to check for a `.set()` method on `T`. But, if the +// `where Ptr: DerefMut` bound is moved to the method, rustc sees the impl block +// as a valid candidate, and doesn't go on to check other candidates when it +// sees that the bound on the method. impl Pin { /// Gets a mutable reference to the pinned value this `Pin` points to. /// @@ -1435,6 +1415,44 @@ impl Pin { unsafe { Pin::new_unchecked(&mut *self.__pointer) } } + /// Gets `Pin<&mut T>` to the underlying pinned value from this nested `Pin`-pointer. + /// + /// This is a generic method to go from `Pin<&mut Pin>>` to `Pin<&mut T>`. It is + /// safe because the existence of a `Pin>` ensures that the pointee, `T`, cannot + /// move in the future, and this method does not enable the pointee to move. "Malicious" + /// implementations of `Ptr::DerefMut` are likewise ruled out by the contract of + /// `Pin::new_unchecked`. + #[unstable(feature = "pin_deref_mut", issue = "86918")] + #[must_use = "`self` will be dropped if the result is not used"] + #[inline(always)] + pub fn as_deref_mut(self: Pin<&mut Pin>) -> Pin<&mut Ptr::Target> { + // SAFETY: What we're asserting here is that going from + // + // Pin<&mut Pin> + // + // to + // + // Pin<&mut Ptr::Target> + // + // is safe. + // + // We need to ensure that two things hold for that to be the case: + // + // 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 + // `Pin`, it must already uphold the pinning guarantees, which must mean that + // `Pin<&mut Ptr::Target>` does as well, since `Pin::as_mut` is safe. We do not have to rely + // on the fact that `Ptr` is _also_ pinned. + // + // For #2, we need to ensure that code given a `Pin<&mut Ptr::Target>` cannot cause the + // `Pin` to move? That is not possible, since `Pin<&mut Ptr::Target>` no longer retains + // any access to the `Ptr` itself, much less the `Pin`. + unsafe { self.get_unchecked_mut() }.as_mut() + } + /// Assigns a new value to the memory location pointed to by the `Pin`. /// /// This overwrites pinned data, but that is okay: the original pinned value's destructor gets @@ -1465,6 +1483,33 @@ impl Pin { } } +impl Pin { + /// Unwraps this `Pin`, returning the underlying `Ptr`. + /// + /// # Safety + /// + /// This function is unsafe. You must guarantee that you will continue to + /// treat the pointer `Ptr` as pinned after you call this function, so that + /// the invariants on the `Pin` type can be upheld. If the code using the + /// resulting `Ptr` does not continue to maintain the pinning invariants that + /// is a violation of the API contract and may lead to undefined behavior in + /// later (safe) operations. + /// + /// Note that you must be able to guarantee that the data pointed to by `Ptr` + /// will be treated as pinned all the way until its `drop` handler is complete! + /// + /// *For more information, see the [`pin` module docs][self]* + /// + /// If the underlying data is [`Unpin`], [`Pin::into_inner`] should be used + /// instead. + #[inline(always)] + #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[stable(feature = "pin_into_inner", since = "1.39.0")] + pub const unsafe fn into_inner_unchecked(pin: Pin) -> Ptr { + pin.__pointer + } +} + impl<'a, T: ?Sized> Pin<&'a T> { /// Constructs a new pin by mapping the interior value. /// @@ -1569,7 +1614,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 +1647,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. @@ -1615,48 +1660,8 @@ impl Pin<&'static T> { } } -impl<'a, Ptr: DerefMut> Pin<&'a mut Pin> { - /// Gets `Pin<&mut T>` to the underlying pinned value from this nested `Pin`-pointer. - /// - /// This is a generic method to go from `Pin<&mut Pin>>` to `Pin<&mut T>`. It is - /// safe because the existence of a `Pin>` ensures that the pointee, `T`, cannot - /// move in the future, and this method does not enable the pointee to move. "Malicious" - /// implementations of `Ptr::DerefMut` are likewise ruled out by the contract of - /// `Pin::new_unchecked`. - #[unstable(feature = "pin_deref_mut", issue = "86918")] - #[must_use = "`self` will be dropped if the result is not used"] - #[inline(always)] - pub fn as_deref_mut(self) -> Pin<&'a mut Ptr::Target> { - // SAFETY: What we're asserting here is that going from - // - // Pin<&mut Pin> - // - // to - // - // Pin<&mut Ptr::Target> - // - // is safe. - // - // 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 - // `Pin<&mut Pin>` - // - // The existence of `Pin` is sufficient to guarantee #1: since we already have a - // `Pin`, it must already uphold the pinning guarantees, which must mean that - // `Pin<&mut Ptr::Target>` does as well, since `Pin::as_mut` is safe. We do not have to rely - // on the fact that `Ptr` is _also_ pinned. - // - // For #2, we need to ensure that code given a `Pin<&mut Ptr::Target>` cannot cause the - // `Pin` to move? That is not possible, since `Pin<&mut Ptr::Target>` no longer retains - // any access to the `Ptr` itself, much less the `Pin`. - unsafe { self.get_unchecked_mut() }.as_mut() - } -} - 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. @@ -1717,10 +1722,56 @@ impl fmt::Pointer for Pin { // for other reasons, though, so we just need to take care not to allow such // impls to land in std. #[stable(feature = "pin", since = "1.33.0")] -impl CoerceUnsized> for Pin where Ptr: CoerceUnsized {} +impl CoerceUnsized> for Pin +where + Ptr: CoerceUnsized + PinCoerceUnsized, + U: PinCoerceUnsized, +{ +} + +#[stable(feature = "pin", since = "1.33.0")] +impl DispatchFromDyn> for Pin +where + Ptr: DispatchFromDyn + PinCoerceUnsized, + U: PinCoerceUnsized, +{ +} + +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +/// Trait that indicates that this is a pointer or a wrapper for one, where +/// unsizing can be performed on the pointee when it is pinned. +/// +/// # Safety +/// +/// If this type implements `Deref`, then the concrete type returned by `deref` +/// and `deref_mut` must not change without a modification. The following +/// operations are not considered modifications: +/// +/// * Moving the pointer. +/// * Performing unsizing coercions on the pointer. +/// * Performing dynamic dispatch with the pointer. +/// * Calling `deref` or `deref_mut` on the pointer. +/// +/// The concrete type of a trait object is the type that the vtable corresponds +/// to. The concrete type of a slice is an array of the same element type and +/// the length specified in the metadata. The concrete type of a sized type +/// is the type itself. +pub unsafe trait PinCoerceUnsized {} + +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl<'a, T: ?Sized> PinCoerceUnsized for &'a T {} + +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl<'a, T: ?Sized> PinCoerceUnsized for &'a mut T {} + +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl PinCoerceUnsized for Pin {} + +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl PinCoerceUnsized for *const T {} #[stable(feature = "pin", since = "1.33.0")] -impl DispatchFromDyn> for Pin where Ptr: DispatchFromDyn {} +unsafe impl PinCoerceUnsized for *mut T {} /// Constructs a [Pin]<[&mut] T>, by pinning a `value: T` locally. /// 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/primitive_docs.rs b/library/core/src/primitive_docs.rs index 5989bcbcc5201..5451e45f6c817 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -832,8 +832,9 @@ mod prim_array {} #[doc(alias = "[")] #[doc(alias = "]")] #[doc(alias = "[]")] -/// A dynamically-sized view into a contiguous sequence, `[T]`. Contiguous here -/// means that elements are laid out so that every element is the same +/// A dynamically-sized view into a contiguous sequence, `[T]`. +/// +/// Contiguous here means that elements are laid out so that every element is the same /// distance from its neighbors. /// /// *[See also the `std::slice` module](crate::slice).* @@ -1127,11 +1128,11 @@ impl (T,) {} #[rustc_doc_primitive = "f16"] #[doc(alias = "half")] -/// A 16-bit floating point type (specifically, the "binary16" type defined in IEEE 754-2008). +/// A 16-bit floating-point type (specifically, the "binary16" type defined in IEEE 754-2008). /// /// This type is very similar to [`prim@f32`] but has decreased precision because it uses half as many -/// bits. Please see [the documentation for [`prim@f32`] or [Wikipedia on -/// half-precision values][wikipedia] for more information. +/// bits. Please see [the documentation for `f32`](prim@f32) or [Wikipedia on half-precision +/// values][wikipedia] for more information. /// /// Note that most common platforms will not support `f16` in hardware without enabling extra target /// features, with the notable exception of Apple Silicon (also known as M1, M2, etc.) processors. @@ -1147,11 +1148,11 @@ mod prim_f16 {} #[rustc_doc_primitive = "f32"] #[doc(alias = "single")] -/// A 32-bit floating point type (specifically, the "binary32" type defined in IEEE 754-2008). +/// A 32-bit floating-point type (specifically, the "binary32" type defined in IEEE 754-2008). /// /// This type can represent a wide range of decimal numbers, like `3.5`, `27`, /// `-113.75`, `0.0078125`, `34359738368`, `0`, `-1`. So unlike integer types -/// (such as `i32`), floating point types can represent non-integer numbers, +/// (such as `i32`), floating-point types can represent non-integer numbers, /// too. /// /// However, being able to represent this wide range of numbers comes at the @@ -1165,8 +1166,8 @@ mod prim_f16 {} /// /// Additionally, `f32` can represent some special values: /// -/// - −0.0: IEEE 754 floating point numbers have a bit that indicates their sign, so −0.0 is a -/// possible value. For comparison −0.0 = +0.0, but floating point operations can carry +/// - −0.0: IEEE 754 floating-point numbers have a bit that indicates their sign, so −0.0 is a +/// possible value. For comparison −0.0 = +0.0, but floating-point operations can carry /// the sign bit through arithmetic operations. This means −0.0 × +0.0 produces −0.0 and /// a negative number rounded to a value smaller than a float can represent also produces −0.0. /// - [∞](#associatedconstant.INFINITY) and @@ -1190,6 +1191,11 @@ mod prim_f16 {} /// portable or even fully deterministic! This means that there may be some /// surprising results upon inspecting the bit patterns, /// as the same calculations might produce NaNs with different bit patterns. +/// This also affects the sign of the NaN: checking `is_sign_positive` or `is_sign_negative` on +/// a NaN is the most common way to run into these surprising results. +/// (Checking `x >= 0.0` or `x <= 0.0` avoids those surprises, but also how negative/positive +/// zero are treated.) +/// See the section below for what exactly is guaranteed about the bit pattern of a NaN. /// /// When a primitive operation (addition, subtraction, multiplication, or /// division) is performed on this type, the result is rounded according to the @@ -1206,44 +1212,122 @@ mod prim_f16 {} /// both arguments were negative, then it is -0.0. Subtraction `a - b` is /// regarded as a sum `a + (-b)`. /// -/// For more information on floating point numbers, see [Wikipedia][wikipedia]. +/// For more information on floating-point numbers, see [Wikipedia][wikipedia]. /// /// *[See also the `std::f32::consts` module](crate::f32::consts).* /// /// [wikipedia]: https://en.wikipedia.org/wiki/Single-precision_floating-point_format +/// +/// # NaN bit patterns +/// +/// This section defines the possible NaN bit patterns returned by floating-point operations. +/// +/// The bit pattern of a floating-point NaN value is defined by: +/// - a sign bit. +/// - a quiet/signaling bit. Rust assumes that the quiet/signaling bit being set to `1` indicates a +/// quiet NaN (QNaN), and a value of `0` indicates a signaling NaN (SNaN). In the following we +/// will hence just call it the "quiet bit". +/// - a payload, which makes up the rest of the significand (i.e., the mantissa) except for the +/// quiet bit. +/// +/// The rules for NaN values differ between *arithmetic* and *non-arithmetic* (or "bitwise") +/// operations. The non-arithmetic operations are unary `-`, `abs`, `copysign`, `signum`, +/// `{to,from}_bits`, `{to,from}_{be,le,ne}_bytes` and `is_sign_{positive,negative}`. These +/// operations are guaranteed to exactly preserve the bit pattern of their input except for possibly +/// changing the sign bit. +/// +/// The following rules apply when a NaN value is returned from an arithmetic operation: +/// - The result has a non-deterministic sign. +/// - The quiet bit and payload are non-deterministically chosen from +/// the following set of options: +/// +/// - **Preferred NaN**: The quiet bit is set and the payload is all-zero. +/// - **Quieting NaN propagation**: The quiet bit is set and the payload is copied from any input +/// operand that is a NaN. If the inputs and outputs do not have the same payload size (i.e., for +/// `as` casts), then +/// - If the output is smaller than the input, low-order bits of the payload get dropped. +/// - If the output is larger than the input, the payload gets filled up with 0s in the low-order +/// bits. +/// - **Unchanged NaN propagation**: The quiet bit and payload are copied from any input operand +/// that is a NaN. If the inputs and outputs do not have the same size (i.e., for `as` casts), the +/// same rules as for "quieting NaN propagation" apply, with one caveat: if the output is smaller +/// than the input, droppig the low-order bits may result in a payload of 0; a payload of 0 is not +/// possible with a signaling NaN (the all-0 significand encodes an infinity) so unchanged NaN +/// propagation cannot occur with some inputs. +/// - **Target-specific NaN**: The quiet bit is set and the payload is picked from a target-specific +/// set of "extra" possible NaN payloads. The set can depend on the input operand values. +/// See the table below for the concrete NaNs this set contains on various targets. +/// +/// In particular, if all input NaNs are quiet (or if there are no input NaNs), then the output NaN +/// is definitely quiet. Signaling NaN outputs can only occur if they are provided as an input +/// value. Similarly, if all input NaNs are preferred (or if there are no input NaNs) and the target +/// does not have any "extra" NaN payloads, then the output NaN is guaranteed to be preferred. +/// +/// The non-deterministic choice happens when the operation is executed; i.e., the result of a +/// NaN-producing floating-point operation is a stable bit pattern (looking at these bits multiple +/// times will yield consistent results), but running the same operation twice with the same inputs +/// can produce different results. +/// +/// These guarantees are neither stronger nor weaker than those of IEEE 754: IEEE 754 guarantees +/// that an operation never returns a signaling NaN, whereas it is possible for operations like +/// `SNAN * 1.0` to return a signaling NaN in Rust. Conversely, IEEE 754 makes no statement at all +/// about which quiet NaN is returned, whereas Rust restricts the set of possible results to the +/// ones listed above. +/// +/// Unless noted otherwise, the same rules also apply to NaNs returned by other library functions +/// (e.g. `min`, `minimum`, `max`, `maximum`); other aspects of their semantics and which IEEE 754 +/// operation they correspond to are documented with the respective functions. +/// +/// When an arithmetic floating-point operation is executed in `const` context, the same rules +/// apply: no guarantee is made about which of the NaN bit patterns described above will be +/// returned. The result does not have to match what happens when executing the same code at +/// runtime, and the result can vary depending on factors such as compiler version and flags. +/// +/// ### Target-specific "extra" NaN values +// FIXME: Is there a better place to put this? +/// +/// | `target_arch` | Extra payloads possible on this platform | +/// |---------------|---------| +/// | `x86`, `x86_64`, `arm`, `aarch64`, `riscv32`, `riscv64` | None | +/// | `sparc`, `sparc64` | The all-one payload | +/// | `wasm32`, `wasm64` | If all input NaNs are quiet with all-zero payload: None.
Otherwise: all possible payloads. | +/// +/// For targets not in this table, all payloads are possible. + #[stable(feature = "rust1", since = "1.0.0")] mod prim_f32 {} #[rustc_doc_primitive = "f64"] #[doc(alias = "double")] -/// A 64-bit floating point type (specifically, the "binary64" type defined in IEEE 754-2008). +/// A 64-bit floating-point type (specifically, the "binary64" type defined in IEEE 754-2008). /// -/// This type is very similar to [`f32`], but has increased -/// precision by using twice as many bits. Please see [the documentation for -/// `f32`][`f32`] or [Wikipedia on double precision +/// This type is very similar to [`prim@f32`], but has increased precision by using twice as many +/// bits. Please see [the documentation for `f32`](prim@f32) or [Wikipedia on double-precision /// values][wikipedia] for more information. /// /// *[See also the `std::f64::consts` module](crate::f64::consts).* /// -/// [`f32`]: prim@f32 /// [wikipedia]: https://en.wikipedia.org/wiki/Double-precision_floating-point_format #[stable(feature = "rust1", since = "1.0.0")] mod prim_f64 {} #[rustc_doc_primitive = "f128"] #[doc(alias = "quad")] -/// A 128-bit floating point type (specifically, the "binary128" type defined in IEEE 754-2008). +/// A 128-bit floating-point type (specifically, the "binary128" type defined in IEEE 754-2008). /// /// This type is very similar to [`prim@f32`] and [`prim@f64`], but has increased precision by using twice -/// as many bits as `f64`. Please see [the documentation for [`prim@f32`] or [Wikipedia on +/// as many bits as `f64`. Please see [the documentation for `f32`](prim@f32) or [Wikipedia on /// quad-precision values][wikipedia] for more information. /// /// Note that no platforms have hardware support for `f128` without enabling target specific features, /// as for all instruction set architectures `f128` is considered an optional feature. -/// Only Power ISA ("PowerPC") and RISCV specify it, and only certain microarchitectures +/// Only Power ISA ("PowerPC") and RISC-V specify it, and only certain microarchitectures /// actually implement it. For x86-64 and AArch64, ISA support is not even specified, /// so it will always be a software implementation significantly slower than `f64`. /// +/// _Note: `f128` support is incomplete. Many platforms will not be able to link math functions. On +/// x86 in particular, these functions do link but their results are always incorrect._ +/// /// *[See also the `std::f128::consts` module](crate::f128::consts).* /// /// [wikipedia]: https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index e5f5bf6a50313..fd1b4f9a45d54 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -1,6 +1,5 @@ -use safety::requires; +use safety::{ensures, requires}; use crate::num::NonZero; -#[cfg(debug_assertions)] use crate::ub_checks::assert_unsafe_precondition; use crate::{cmp, fmt, hash, mem, num}; @@ -48,6 +47,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 +61,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,10 +83,10 @@ 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!( check_language_ub, "Alignment::new_unchecked requires a power of two", @@ -99,6 +102,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 +111,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 +134,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 +167,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 +382,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 3e7933e9eec86..3b635e2a4aa9e 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 @@ -239,24 +239,7 @@ impl *const T { /// # Safety /// /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// all of the following is true: - /// - /// * The pointer must be properly aligned. - /// - /// * It must be "dereferenceable" in the sense defined in [the module documentation]. - /// - /// * The pointer must point to an initialized instance of `T`. - /// - /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is - /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. - /// In particular, while this reference exists, the memory the pointer points to must - /// not get mutated (except inside `UnsafeCell`). - /// - /// This applies even if the result of this method is unused! - /// (The part about being initialized is not yet fully decided, but until - /// it is, the only safe approach is to ensure that they are indeed initialized.) - /// - /// [the module documentation]: crate::ptr#safety + /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// /// # Examples /// @@ -302,24 +285,8 @@ impl *const T { /// /// # Safety /// - /// When calling this method, you have to ensure that all of the following is true: - /// - /// * The pointer must be properly aligned. - /// - /// * It must be "dereferenceable" in the sense defined in [the module documentation]. - /// - /// * The pointer must point to an initialized instance of `T`. - /// - /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is - /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. - /// In particular, while this reference exists, the memory the pointer points to must - /// not get mutated (except inside `UnsafeCell`). - /// - /// This applies even if the result of this method is unused! - /// (The part about being initialized is not yet fully decided, but until - /// it is, the only safe approach is to ensure that they are indeed initialized.) - /// - /// [the module documentation]: crate::ptr#safety + /// When calling this method, you have to ensure that + /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// /// # Examples /// @@ -350,20 +317,7 @@ impl *const T { /// # Safety /// /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// all of the following is true: - /// - /// * The pointer must be properly aligned. - /// - /// * It must be "dereferenceable" in the sense defined in [the module documentation]. - /// - /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is - /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. - /// In particular, while this reference exists, the memory the pointer points to must - /// not get mutated (except inside `UnsafeCell`). - /// - /// This applies even if the result of this method is unused! - /// - /// [the module documentation]: crate::ptr#safety + /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// /// # Examples /// diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index eb86bf6620652..ccc9f8754f00c 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -2,9 +2,9 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; -use crate::intrinsics::aggregate_raw_ptr; -use crate::intrinsics::ptr_metadata; +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. /// @@ -80,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`. @@ -153,7 +153,7 @@ pub const fn from_raw_parts_mut( /// 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, } @@ -166,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. diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index af6ae0a23a45c..43d81348cb1a8 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -56,6 +56,44 @@ //! has size 0, i.e., even if memory is not actually touched. Consider using //! [`NonNull::dangling`] in such cases. //! +//! ## Pointer to reference conversion +//! +//! When converting a pointer to a reference (e.g. via `&*ptr` or `&mut *ptr`), +//! there are several rules that must be followed: +//! +//! * The pointer must be properly aligned. +//! +//! * It must be non-null. +//! +//! * It must be "dereferenceable" in the sense defined above. +//! +//! * The pointer must point to a [valid value] of type `T`. +//! +//! * You must enforce Rust's aliasing rules. The exact aliasing rules are not decided yet, so we +//! only give a rough overview here. The rules also depend on whether a mutable or a shared +//! reference is being created. +//! * When creating a mutable reference, then while this reference exists, the memory it points to +//! must not get accessed (read or written) through any other pointer or reference not derived +//! from this reference. +//! * When creating a shared reference, then while this reference exists, the memory it points to +//! must not get mutated (except inside `UnsafeCell`). +//! +//! If a pointer follows all of these rules, it is said to be +//! *convertible to a (mutable or shared) reference*. +// ^ we use this term instead of saying that the produced reference must +// be valid, as the validity of a reference is easily confused for the +// validity of the thing it refers to, and while the two concepts are +// closly related, they are not identical. +//! +//! These rules apply even if the result is unused! +//! (The part about being initialized is not yet fully decided, but until +//! it is, the only safe approach is to ensure that they are indeed initialized.) +//! +//! An example of the implications of the above rules is that an expression such +//! as `unsafe { &*(0 as *const u8) }` is Immediate Undefined Behavior. +//! +//! [valid value]: ../../reference/behavior-considered-undefined.html#invalid-values +//! //! ## Allocated object //! //! An *allocated object* is a subset of program memory which is addressable @@ -196,9 +234,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 +272,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)] @@ -409,13 +447,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 +460,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; @@ -608,7 +641,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. @@ -652,7 +685,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. @@ -689,7 +722,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 @@ -737,7 +770,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 @@ -777,10 +810,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. +/// +/// 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. /// -/// 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. +/// ## 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")] @@ -791,10 +867,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")] @@ -1390,7 +1503,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; @@ -1601,7 +1714,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; @@ -1809,6 +1922,39 @@ pub unsafe fn write_volatile(dst: *mut T, src: T) { /// /// Any questions go to @nagisa. #[lang = "align_offset"] +#[safety::requires(a.is_power_of_two())] +#[safety::ensures(|result| { + let stride = mem::size_of::(); + // ZSTs + if stride == 0 { + if p.addr() % a == 0 { + return *result == 0; + } else { + return *result == usize::MAX; + } + } + + // In this case, the pointer cannot be aligned + if (a % stride == 0) && (p.addr() % stride != 0) { + return *result == usize::MAX; + } + + // Checking if the answer should indeed be usize::MAX when a % stride != 0 + // requires computing gcd(a, stride), which is too expensive without + // quantifiers (https://model-checking.github.io/kani/rfc/rfcs/0010-quantifiers.html). + // This should be updated once quantifiers are available. + if (a % stride != 0 && *result == usize::MAX) { + return true; + } + + // If we reach this case, either: + // - a % stride == 0 and p.addr() % stride == 0, so it is definitely possible to align the pointer + // - a % stride != 0 and result != usize::MAX, so align_offset is claiming that it's possible to align the pointer + // Check that applying the returned result does indeed produce an aligned address + let product = usize::wrapping_mul(*result, stride); + let new_addr = usize::wrapping_add(product, p.addr()); + *result != usize::MAX && new_addr % a == 0 +})] pub(crate) const unsafe fn align_offset(p: *const T, a: usize) -> usize { // FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <= // 1, where the method versions of these operations are not inlined. @@ -1938,7 +2084,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 { @@ -2060,6 +2206,33 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { (p as *const ()) == (q as *const ()) } +/// Compares the *addresses* of the two function pointers for equality. +/// +/// Function pointers comparisons can have surprising results since +/// they are never guaranteed to be unique and could vary between different +/// code generation units. Furthermore, different functions could have the +/// same address after being merged together. +/// +/// This is the same as `f == g` but using this function makes clear +/// that you are aware of these potentially surprising semantics. +/// +/// # Examples +/// +/// ``` +/// #![feature(ptr_fn_addr_eq)] +/// use std::ptr; +/// +/// fn a() { println!("a"); } +/// fn b() { println!("b"); } +/// assert!(!ptr::fn_addr_eq(a as fn(), b as fn())); +/// ``` +#[unstable(feature = "ptr_fn_addr_eq", issue = "129322")] +#[inline(always)] +#[must_use = "function pointer comparison produces a value"] +pub fn fn_addr_eq(f: T, g: U) -> bool { + f.addr() == g.addr() +} + /// Hash a raw pointer. /// /// This can be used to hash a `&T` reference (which coerces to `*const T` implicitly) @@ -2137,7 +2310,18 @@ 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. +/// +/// `addr_of!(expr)` is equivalent to `&raw const expr`. The macro is *soft-deprecated*; +/// use `&raw const` instead. +/// +/// It is still an open question under which conditions writing through an `addr_of!`-created +/// pointer is permitted. If the place `expr` evaluates to is based on a raw pointer, then the +/// result of `addr_of!` inherits all permissions from that raw pointer. However, if the place is +/// based on a reference, local variable, or `static`, then until all details are decided, the same +/// rules as for shared references apply: it is UB to write through a pointer created with this +/// operation, except for bytes located inside an `UnsafeCell`. Use `&raw mut` (or [`addr_of_mut`]) +/// to create a raw pointer that definitely permits mutation. /// /// 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, @@ -2211,7 +2395,10 @@ 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. +/// +/// `addr_of_mut!(expr)` is equivalent to `&raw mut expr`. The macro is *soft-deprecated*; +/// use `&raw mut` instead. /// /// 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, @@ -2305,6 +2492,9 @@ mod verify { use crate::fmt::Debug; use super::*; use crate::kani; + use intrinsics::{ + mul_with_overflow, unchecked_sub, wrapping_mul, wrapping_sub + }; #[kani::proof_for_contract(read_volatile)] pub fn check_read_u128() { @@ -2313,4 +2503,120 @@ mod verify { let copy = unsafe { read_volatile(ptr) }; assert_eq!(val, copy); } + + fn check_align_offset(p: *const T) { + let a = kani::any::(); + unsafe { align_offset(p, a) }; + } + + #[kani::proof_for_contract(align_offset)] + fn check_align_offset_zst() { + let p = kani::any::() as *const (); + check_align_offset(p); + } + + #[kani::proof_for_contract(align_offset)] + fn check_align_offset_u8() { + let p = kani::any::() as *const u8; + check_align_offset(p); + } + + #[kani::proof_for_contract(align_offset)] + fn check_align_offset_u16() { + let p = kani::any::() as *const u16; + check_align_offset(p); + } + + #[kani::proof_for_contract(align_offset)] + fn check_align_offset_u32() { + let p = kani::any::() as *const u32; + check_align_offset(p); + } + + #[kani::proof_for_contract(align_offset)] + fn check_align_offset_u64() { + let p = kani::any::() as *const u64; + check_align_offset(p); + } + + #[kani::proof_for_contract(align_offset)] + fn check_align_offset_u128() { + let p = kani::any::() as *const u128; + check_align_offset(p); + } + + #[kani::proof_for_contract(align_offset)] + fn check_align_offset_4096() { + let p = kani::any::() as *const [u128; 64]; + check_align_offset(p); + } + + #[kani::proof_for_contract(align_offset)] + fn check_align_offset_17() { + let p = kani::any::() as *const [char; 17]; + check_align_offset(p); + } + + // This function lives inside align_offset, so it is not publicly accessible (hence this copy). + #[safety::requires(m.is_power_of_two())] + #[safety::requires(x < m)] + // TODO: add ensures contract to check that the answer is indeed correct + // This will require quantifiers (https://model-checking.github.io/kani/rfc/rfcs/0010-quantifiers.html) + // so that we can add a precondition that gcd(x, m) = 1 like so: + // ∀d, d > 0 ∧ x % d = 0 ∧ m % d = 0 → d = 1 + // With this precondition, we can then write this postcondition to check the correctness of the answer: + // #[safety::ensures(|result| wrapping_mul(*result, x) % m == 1)] + const unsafe fn mod_inv_copy(x: usize, m: usize) -> usize { + /// Multiplicative modular inverse table modulo 2⁴ = 16. + /// + /// Note, that this table does not contain values where inverse does not exist (i.e., for + /// `0⁻¹ mod 16`, `2⁻¹ mod 16`, etc.) + const INV_TABLE_MOD_16: [u8; 8] = [1, 11, 13, 7, 9, 3, 5, 15]; + /// Modulo for which the `INV_TABLE_MOD_16` is intended. + const INV_TABLE_MOD: usize = 16; + + // SAFETY: `m` is required to be a power-of-two, hence non-zero. + let m_minus_one = unsafe { unchecked_sub(m, 1) }; + let mut inverse = INV_TABLE_MOD_16[(x & (INV_TABLE_MOD - 1)) >> 1] as usize; + let mut mod_gate = INV_TABLE_MOD; + // We iterate "up" using the following formula: + // + // $$ xy ≡ 1 (mod 2ⁿ) → xy (2 - xy) ≡ 1 (mod 2²ⁿ) $$ + // + // This application needs to be applied at least until `2²ⁿ ≥ m`, at which point we can + // finally reduce the computation to our desired `m` by taking `inverse mod m`. + // + // This computation is `O(log log m)`, which is to say, that on 64-bit machines this loop + // will always finish in at most 4 iterations. + loop { + // y = y * (2 - xy) mod n + // + // Note, that we use wrapping operations here intentionally – the original formula + // uses e.g., subtraction `mod n`. It is entirely fine to do them `mod + // usize::MAX` instead, because we take the result `mod n` at the end + // anyway. + if mod_gate >= m { + break; + } + inverse = wrapping_mul(inverse, wrapping_sub(2usize, wrapping_mul(x, inverse))); + let (new_gate, overflow) = mul_with_overflow(mod_gate, mod_gate); + if overflow { + break; + } + mod_gate = new_gate; + } + inverse & m_minus_one + } + + // The specification for mod_inv states that it cannot ever panic. + // Verify that is the case, given that the function's safety preconditions are met. + + // TODO: Once https://github.com/model-checking/kani/issues/3467 is fixed, + // move this harness inside `align_offset` and delete `mod_inv_copy` + #[kani::proof_for_contract(mod_inv_copy)] + fn check_mod_inv() { + let x = kani::any::(); + let m = kani::any::(); + unsafe { mod_inv_copy(x, m) }; + } } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 904d6c62dcf1e..42975cc927b8e 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 @@ -247,24 +247,7 @@ impl *mut T { /// # Safety /// /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// all of the following is true: - /// - /// * The pointer must be properly aligned. - /// - /// * It must be "dereferenceable" in the sense defined in [the module documentation]. - /// - /// * The pointer must point to an initialized instance of `T`. - /// - /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is - /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. - /// In particular, while this reference exists, the memory the pointer points to must - /// not get mutated (except inside `UnsafeCell`). - /// - /// This applies even if the result of this method is unused! - /// (The part about being initialized is not yet fully decided, but until - /// it is, the only safe approach is to ensure that they are indeed initialized.) - /// - /// [the module documentation]: crate::ptr#safety + /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// /// # Examples /// @@ -313,24 +296,7 @@ impl *mut T { /// /// # Safety /// - /// When calling this method, you have to ensure that all of the following is true: - /// - /// * The pointer must be properly aligned. - /// - /// * It must be "dereferenceable" in the sense defined in [the module documentation]. - /// - /// * The pointer must point to an initialized instance of `T`. - /// - /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is - /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. - /// In particular, while this reference exists, the memory the pointer points to must - /// not get mutated (except inside `UnsafeCell`). - /// - /// This applies even if the result of this method is unused! - /// (The part about being initialized is not yet fully decided, but until - /// it is, the only safe approach is to ensure that they are indeed initialized.) - /// - /// [the module documentation]: crate::ptr#safety + /// When calling this method, you have to ensure that the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// /// # Examples /// @@ -364,20 +330,9 @@ impl *mut T { /// # Safety /// /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// all of the following is true: - /// - /// * The pointer must be properly aligned. - /// - /// * It must be "dereferenceable" in the sense defined in [the module documentation]. - /// - /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is - /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. - /// In particular, while this reference exists, the memory the pointer points to must - /// not get mutated (except inside `UnsafeCell`). - /// - /// This applies even if the result of this method is unused! - /// - /// [the module documentation]: crate::ptr#safety + /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). + /// Note that because the created reference is to `MaybeUninit`, the + /// source pointer can point to uninitialized memory. /// /// # Examples /// @@ -609,25 +564,10 @@ impl *mut T { /// /// # Safety /// - /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// all of the following is true: - /// - /// * The pointer must be properly aligned. - /// - /// * It must be "dereferenceable" in the sense defined in [the module documentation]. - /// - /// * The pointer must point to an initialized instance of `T`. + /// When calling this method, you have to ensure that *either* + /// the pointer is null *or* + /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// - /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is - /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. - /// In particular, while this reference exists, the memory the pointer points to must - /// not get accessed (read or written) through any other pointer. - /// - /// This applies even if the result of this method is unused! - /// (The part about being initialized is not yet fully decided, but until - /// it is, the only safe approach is to ensure that they are indeed initialized.) - /// - /// [the module documentation]: crate::ptr#safety /// /// # Examples /// @@ -675,24 +615,8 @@ impl *mut T { /// /// # Safety /// - /// When calling this method, you have to ensure that all of the following is true: - /// - /// * The pointer must be properly aligned. - /// - /// * It must be "dereferenceable" in the sense defined in [the module documentation]. - /// - /// * The pointer must point to an initialized instance of `T`. - /// - /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is - /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. - /// In particular, while this reference exists, the memory the pointer points to must - /// not get mutated (except inside `UnsafeCell`). - /// - /// This applies even if the result of this method is unused! - /// (The part about being initialized is not yet fully decided, but until - /// it is, the only safe approach is to ensure that they are indeed initialized.) - /// - /// [the module documentation]: crate::ptr#safety + /// When calling this method, you have to ensure that + /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// /// # Examples /// @@ -727,20 +651,7 @@ impl *mut T { /// # Safety /// /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// all of the following is true: - /// - /// * The pointer must be properly aligned. - /// - /// * It must be "dereferenceable" in the sense defined in [the module documentation]. - /// - /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is - /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. - /// In particular, while this reference exists, the memory the pointer points to must - /// not get accessed (read or written) through any other pointer. - /// - /// This applies even if the result of this method is unused! - /// - /// [the module documentation]: crate::ptr#safety + /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). #[inline] #[unstable(feature = "ptr_as_uninit", issue = "75402")] #[rustc_const_unstable(feature = "const_ptr_as_ref", issue = "91822")] diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 796c85d0cacc7..b1429fff74434 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1,15 +1,13 @@ 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::pin::PinCoerceUnsized; 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]. /// @@ -128,20 +126,10 @@ impl NonNull { /// /// # Safety /// - /// When calling this method, you have to ensure that all of the following is true: - /// - /// * The pointer must be properly aligned. - /// - /// * It must be "dereferenceable" in the sense defined in [the module documentation]. - /// - /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is - /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. - /// In particular, while this reference exists, the memory the pointer points to must - /// not get mutated (except inside `UnsafeCell`). - /// - /// This applies even if the result of this method is unused! - /// - /// [the module documentation]: crate::ptr#safety + /// When calling this method, you have to ensure that + /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). + /// Note that because the created reference is to `MaybeUninit`, the + /// source pointer can point to uninitialized memory. #[inline] #[must_use] #[unstable(feature = "ptr_as_uninit", issue = "75402")] @@ -162,20 +150,10 @@ impl NonNull { /// /// # Safety /// - /// When calling this method, you have to ensure that all of the following is true: - /// - /// * The pointer must be properly aligned. - /// - /// * It must be "dereferenceable" in the sense defined in [the module documentation]. - /// - /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is - /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. - /// In particular, while this reference exists, the memory the pointer points to must - /// not get accessed (read or written) through any other pointer. - /// - /// This applies even if the result of this method is unused! - /// - /// [the module documentation]: crate::ptr#safety + /// When calling this method, you have to ensure that + /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). + /// Note that because the created reference is to `MaybeUninit`, the + /// source pointer can point to uninitialized memory. #[inline] #[must_use] #[unstable(feature = "ptr_as_uninit", issue = "75402")] @@ -361,22 +339,8 @@ impl NonNull { /// /// # Safety /// - /// When calling this method, you have to ensure that all of the following is true: - /// - /// * The pointer must be properly aligned. - /// - /// * It must be "dereferenceable" in the sense defined in [the module documentation]. - /// - /// * The pointer must point to an initialized instance of `T`. - /// - /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is - /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. - /// In particular, while this reference exists, the memory the pointer points to must - /// not get mutated (except inside `UnsafeCell`). - /// - /// This applies even if the result of this method is unused! - /// (The part about being initialized is not yet fully decided, but until - /// it is, the only safe approach is to ensure that they are indeed initialized.) + /// When calling this method, you have to ensure that + /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// /// # Examples /// @@ -412,22 +376,8 @@ impl NonNull { /// /// # Safety /// - /// When calling this method, you have to ensure that all of the following is true: - /// - /// * The pointer must be properly aligned. - /// - /// * It must be "dereferenceable" in the sense defined in [the module documentation]. - /// - /// * The pointer must point to an initialized instance of `T`. - /// - /// * You must enforce Rust's aliasing rules, since the returned lifetime `'a` is - /// arbitrarily chosen and does not necessarily reflect the actual lifetime of the data. - /// In particular, while this reference exists, the memory the pointer points to must - /// not get accessed (read or written) through any other pointer. - /// - /// This applies even if the result of this method is unused! - /// (The part about being initialized is not yet fully decided, but until - /// it is, the only safe approach is to ensure that they are indeed initialized.) + /// When calling this method, you have to ensure that + /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). /// # Examples /// /// ``` @@ -1171,9 +1121,7 @@ impl NonNull { /// `align`. /// /// If it is not possible to align the pointer, the implementation returns - /// `usize::MAX`. It is permissible for the implementation to *always* - /// return `usize::MAX`. Only your algorithm's performance can depend - /// on getting a usable offset here, not its correctness. + /// `usize::MAX`. /// /// The offset is expressed in number of `T` elements, and not bytes. /// @@ -1181,6 +1129,15 @@ impl NonNull { /// beyond the allocation that the pointer points into. It is up to the caller to ensure that /// the returned offset is correct in all terms other than alignment. /// + /// When this is called during compile-time evaluation (which is unstable), the implementation + /// may return `usize::MAX` in cases where that can never happen at runtime. This is because the + /// actual alignment of pointers is not known yet during compile-time, so an offset with + /// guaranteed alignment can sometimes not be computed. For example, a buffer declared as `[u8; + /// N]` might be allocated at an odd or an even address, but at compile-time this is not yet + /// known, so the execution has to be correct for either choice. It is therefore impossible to + /// find an offset that is guaranteed to be 2-aligned. (This behavior is subject to change, as usual + /// for unstable APIs.) + /// /// # Panics /// /// The function panics if `align` is not a power-of-two. @@ -1727,6 +1684,9 @@ impl CoerceUnsized> for NonNull where T: Uns #[unstable(feature = "dispatch_from_dyn", issue = "none")] impl DispatchFromDyn> for NonNull where T: Unsize {} +#[stable(feature = "pin", since = "1.33.0")] +unsafe impl PinCoerceUnsized for NonNull {} + #[stable(feature = "nonnull", since = "1.25.0")] impl fmt::Debug for NonNull { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index b74d691e45427..8a282c86a7868 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -1,8 +1,13 @@ +use safety::{ensures, requires}; use crate::fmt; use crate::marker::{PhantomData, Unsize}; use crate::ops::{CoerceUnsized, DispatchFromDyn}; +use crate::pin::PinCoerceUnsized; 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 +89,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 +98,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 +111,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 +119,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 } @@ -166,6 +177,9 @@ impl CoerceUnsized> for Unique where T: Unsiz #[unstable(feature = "ptr_internals", issue = "none")] impl DispatchFromDyn> for Unique where T: Unsize {} +#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +unsafe impl PinCoerceUnsized for Unique {} + #[unstable(feature = "ptr_internals", issue = "none")] impl fmt::Debug for Unique { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -201,3 +215,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] + 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 index bfbbf123b1ca5..408972c267f1a 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -25,15 +25,13 @@ mod iter; pub mod legacy; #[doc(inline)] -pub use crate::ops::{Bound, OneSidedRange, RangeBounds, RangeFull, RangeTo, RangeToInclusive}; - +pub use iter::{IterRange, IterRangeFrom, IterRangeInclusive}; use Bound::{Excluded, Included, Unbounded}; #[doc(inline)] pub use crate::iter::Step; - #[doc(inline)] -pub use iter::{IterRange, IterRangeFrom, IterRangeInclusive}; +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). @@ -72,7 +70,7 @@ impl fmt::Debug for Range { } impl Range { - /// Create an iterator over the elements within this range. + /// Creates an iterator over the elements within this range. /// /// Shorthand for `.clone().into_iter()` /// @@ -292,7 +290,7 @@ impl> RangeInclusive { } impl RangeInclusive { - /// Create an iterator over the elements within this range. + /// Creates an iterator over the elements within this range. /// /// Shorthand for `.clone().into_iter()` /// @@ -408,7 +406,7 @@ impl fmt::Debug for RangeFrom { } impl RangeFrom { - /// Create an iterator over the elements within this range. + /// Creates an iterator over the elements within this range. /// /// Shorthand for `.clone().into_iter()` /// diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index 2b7db475ffb2c..4935280df60bc 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -1,9 +1,8 @@ -use crate::num::NonZero; -use crate::range::{legacy, Range, RangeFrom, RangeInclusive}; - 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")] diff --git a/library/core/src/result.rs b/library/core/src/result.rs index f8cdcc000c50e..73b11f803d929 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -1481,7 +1481,6 @@ impl Result { #[track_caller] #[stable(feature = "option_result_unwrap_unchecked", since = "1.58.0")] pub unsafe fn unwrap_unchecked(self) -> T { - debug_assert!(self.is_ok()); match self { Ok(t) => t, // SAFETY: the safety contract must be upheld by the caller. @@ -1513,7 +1512,6 @@ impl Result { #[track_caller] #[stable(feature = "option_result_unwrap_unchecked", since = "1.58.0")] pub unsafe fn unwrap_err_unchecked(self) -> E { - debug_assert!(self.is_err()); match self { // SAFETY: the safety contract must be upheld by the caller. Ok(_) => unsafe { hint::unreachable_unchecked() }, @@ -1990,7 +1988,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 bf444d2f68af8..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. diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 5bee4d551352e..1769612def0a5 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -1,11 +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; +use crate::num::NonZero; +use crate::{ascii, mem}; #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq<[U]> for [T] @@ -184,19 +183,41 @@ impl SliceOrd for A { } } -// `compare_bytes` compares a sequence of unsigned bytes lexicographically. -// this matches the order we want for [u8], but no others (not even [i8]). -impl SliceOrd for u8 { +/// Marks that a type should be treated as an unsigned byte for comparisons. +/// +/// # Safety +/// * The type must be readable as an `u8`, meaning it has to have the same +/// layout as `u8` and always be initialized. +/// * For every `x` and `y` of this type, `Ord(x, y)` must return the same +/// value as `Ord::cmp(transmute::<_, u8>(x), transmute::<_, u8>(y))`. +#[rustc_specialization_trait] +unsafe trait UnsignedBytewiseOrd {} + +unsafe impl UnsignedBytewiseOrd for bool {} +unsafe impl UnsignedBytewiseOrd for u8 {} +unsafe impl UnsignedBytewiseOrd for NonZero {} +unsafe impl UnsignedBytewiseOrd for Option> {} +unsafe impl UnsignedBytewiseOrd for ascii::Char {} + +// `compare_bytes` compares a sequence of unsigned bytes lexicographically, so +// use it if the requirements for `UnsignedBytewiseOrd` are fulfilled. +impl SliceOrd for A { #[inline] fn compare(left: &[Self], right: &[Self]) -> Ordering { - // Since the length of a slice is always less than or equal to isize::MAX, this never underflows. + // Since the length of a slice is always less than or equal to + // isize::MAX, this never underflows. let diff = left.len() as isize - right.len() as isize; - // This comparison gets optimized away (on x86_64 and ARM) because the subtraction updates flags. + // This comparison gets optimized away (on x86_64 and ARM) because the + // subtraction updates flags. let len = if left.len() < right.len() { left.len() } else { right.len() }; - // SAFETY: `left` and `right` are references and are thus guaranteed to be valid. - // We use the minimum of both lengths which guarantees that both regions are - // valid for reads in that interval. - let mut order = unsafe { compare_bytes(left.as_ptr(), right.as_ptr(), len) as isize }; + let left = left.as_ptr().cast(); + let right = right.as_ptr().cast(); + // SAFETY: `left` and `right` are references and are thus guaranteed to + // be valid. `UnsignedBytewiseOrd` is only implemented for types that + // are valid u8s and can be compared the same way. We use the minimum + // of both lengths which guarantees that both regions are valid for + // reads in that interval. + let mut order = unsafe { compare_bytes(left, right, len) as isize }; if order == 0 { order = diff; } diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 2624a44bb4bcb..de1492e82ce7d 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -1,9 +1,8 @@ //! Indexing implementations for `[T]`. use crate::intrinsics::const_eval_select; -use crate::ops; -use crate::range; use crate::ub_checks::assert_unsafe_precondition; +use crate::{ops, range}; #[stable(feature = "rust1", since = "1.0.0")] impl ops::Index for [T] @@ -214,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. /// @@ -223,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. /// @@ -802,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 @@ -898,7 +899,7 @@ 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. /// @@ -951,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), @@ -970,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, @@ -995,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 504676ce187a8..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,8 +12,7 @@ use crate::marker::PhantomData; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; use crate::ptr::{self, without_provenance, without_provenance_mut, NonNull}; - -use super::{from_raw_parts, from_raw_parts_mut}; +use crate::{cmp, fmt}; #[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] impl !Iterator for [T] {} diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 68508e85f8e14..166189f4b6cf3 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", @@ -31,6 +28,7 @@ pub mod memchr; issue = "none", reason = "exposed from core to be reused in std;" )] +#[doc(hidden)] pub mod sort; mod ascii; @@ -44,52 +42,38 @@ 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}; - -#[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. /// @@ -321,7 +305,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`. /// @@ -350,7 +334,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`. /// @@ -381,7 +365,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`. /// @@ -413,7 +397,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`. @@ -451,7 +435,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`. /// @@ -483,7 +467,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`. @@ -521,7 +505,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`. /// @@ -539,7 +523,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 @@ -554,7 +538,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`. /// @@ -726,7 +710,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 @@ -761,7 +745,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. @@ -828,7 +812,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 @@ -839,7 +823,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 } @@ -2787,41 +2771,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,9 +2881,19 @@ impl [T] { /// 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. + /// If the implementation of [`Ord`] for `T` does not implement a [total order] the resulting + /// order of elements in the slice 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 implementation of [`Ord`] for `T` panics. + /// + /// Sorting types that only implement [`PartialOrd`] such as [`f32`] and [`f64`] require + /// additional precautions. For example, `f32::NAN != f32::NAN`, which doesn't fulfill the + /// reflexivity requirement of [`Ord`]. By using an alternative comparison function with + /// `slice::sort_unstable_by` such as [`f32::total_cmp`] or [`f64::total_cmp`] that defines a + /// [total order] users can sort slices containing floating-point values. Alternatively, if all + /// values in the slice are guaranteed to be in a subset for which [`PartialOrd::partial_cmp`] + /// forms a [total order], it's possible to sort the slice with `sort_unstable_by(|a, b| + /// a.partial_cmp(b).unwrap())`. /// /// # Current implementation /// @@ -2898,18 +2905,21 @@ impl [T] { /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. /// - /// If `T: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [-5, 4, 1, -3, 2]; + /// let mut v = [4, -5, 1, -3, 2]; /// /// v.sort_unstable(); - /// assert!(v == [-5, -3, 1, 2, 4]); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable(&mut self) @@ -2919,31 +2929,20 @@ impl [T] { sort::unstable::sort(self, &mut T::lt); } - /// Sorts the slice with a comparator function, **without** preserving the initial order of + /// Sorts the slice with a comparison 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. /// - /// 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 `>`. - /// - /// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use - /// `partial_cmp` as our sort function when we know the slice doesn't contain a `NaN`. + /// If the comparison function `compare` does not implement a [total order] the resulting order + /// of elements in the slice 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 `compare` panics. /// - /// ``` - /// let mut floats = [5f64, 4.0, 1.0, 3.0, 2.0]; - /// floats.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); - /// assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 5.0]); - /// ``` + /// For example `|a, b| (a - b).cmp(a)` is a comparison function that is neither transitive nor + /// reflexive nor total, `a < b < c < a` with `a = 1, b = 2, c = 3`. For more information and + /// examples see the [`Ord`] documentation. /// /// # Current implementation /// @@ -2955,21 +2954,24 @@ impl [T] { /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. /// - /// If `T: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if `compare` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [5, 4, 1, 3, 2]; + /// let mut v = [4, -5, 1, -3, 2]; /// v.sort_unstable_by(|a, b| a.cmp(b)); - /// assert!(v == [1, 2, 3, 4, 5]); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); /// /// // reverse sorting /// v.sort_unstable_by(|a, b| b.cmp(a)); - /// assert!(v == [5, 4, 3, 2, 1]); + /// assert_eq!(v, [4, 2, 1, -3, -5]); /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable_by(&mut self, mut compare: F) @@ -2985,9 +2987,10 @@ impl [T] { /// 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 `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. + /// If the implementation of [`Ord`] for `K` does not implement a [total order] the resulting + /// order of elements in the slice 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 implementation of [`Ord`] for `K` panics. /// /// # Current implementation /// @@ -2999,18 +3002,21 @@ impl [T] { /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. /// - /// If `K: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [-5i32, 4, 1, -3, 2]; + /// let mut v = [4i32, -5, 1, -3, 2]; /// /// v.sort_unstable_by_key(|k| k.abs()); - /// assert!(v == [1, 2, -3, 4, -5]); + /// assert_eq!(v, [1, 2, -3, 4, -5]); /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable_by_key(&mut self, mut f: F) @@ -3021,7 +3027,7 @@ impl [T] { 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 + /// 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 @@ -3042,15 +3048,14 @@ impl [T] { /// 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 /// /// # Panics /// /// Panics when `index >= len()`, meaning it always panics on empty slices. /// + /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order]. + /// /// # Examples /// /// ``` @@ -3073,6 +3078,7 @@ impl [T] { /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[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]) @@ -3082,7 +3088,7 @@ impl [T] { sort::select::partition_at_index(self, index, T::lt) } - /// Reorder the slice with a comparator function such that the element at `index` after the + /// 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 @@ -3103,15 +3109,14 @@ impl [T] { /// 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 /// /// # Panics /// /// Panics when `index >= len()`, meaning it always panics on empty slices. /// + /// May panic if `compare` does not implement a [total order]. + /// /// # Examples /// /// ``` @@ -3134,6 +3139,7 @@ impl [T] { /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] pub fn select_nth_unstable_by( @@ -3147,7 +3153,7 @@ impl [T] { 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 + /// 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 @@ -3168,15 +3174,14 @@ impl [T] { /// 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 /// /// # Panics /// /// Panics when `index >= len()`, meaning it always panics on empty slices. /// + /// May panic if `K: Ord` does not implement a total order. + /// /// # Examples /// /// ``` @@ -3199,6 +3204,7 @@ impl [T] { /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] pub fn select_nth_unstable_by_key( @@ -3405,8 +3411,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 /// @@ -3448,8 +3456,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 /// @@ -3657,8 +3667,8 @@ impl [T] { { // The panic code path was put into a cold function to not bloat the // call site. - #[inline(never)] - #[cold] + #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] + #[cfg_attr(feature = "panic_immediate_abort", inline)] #[track_caller] fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! { panic!( @@ -3819,7 +3829,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 @@ -3884,7 +3894,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 @@ -3957,7 +3967,7 @@ impl [T] { } } - /// Split a slice into a prefix, a middle of aligned SIMD types, and a suffix. + /// Splits a slice into a prefix, a middle of aligned SIMD types, and a suffix. /// /// This is a safe wrapper around [`slice::align_to`], so inherits the same /// guarantees as that method. @@ -4021,7 +4031,7 @@ 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 inherits the same @@ -4069,7 +4079,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(is_sorted)] /// let empty: [i32; 0] = []; /// /// assert!([1, 2, 2, 9].is_sorted()); @@ -4079,7 +4088,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 = "1.82.0")] #[must_use] pub fn is_sorted(&self) -> bool where @@ -4096,8 +4105,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)); /// @@ -4108,7 +4115,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 = "1.82.0")] #[must_use] pub fn is_sorted_by<'a, F>(&'a self, mut compare: F) -> bool where @@ -4128,13 +4135,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 = "1.82.0")] #[must_use] pub fn is_sorted_by_key<'a, F, K>(&'a self, f: F) -> bool where @@ -4522,6 +4527,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]] { diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 280aead270e76..2cf3fecb47542 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -1,9 +1,7 @@ //! Free functions to create `&[T]` and `&mut [T]`. -use crate::array; 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. /// @@ -13,13 +11,13 @@ use crate::ub_checks; /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `data` must be [valid] for reads for `len * mem::size_of::()` many bytes, +/// * `data` must be non-null, [valid] for reads for `len * mem::size_of::()` many bytes, /// and it must be properly aligned. This means in particular: /// /// * The entire memory range of this slice must be contained within a single allocated object! /// Slices can never span across multiple allocated objects. See [below](#incorrect-usage) /// for an example incorrectly not taking this into account. -/// * `data` must be non-null and aligned even for zero-length slices. One +/// * `data` must be non-null and aligned even for zero-length slices or slices of ZSTs. One /// reason for this is that enum layout optimizations may rely on references /// (including slices of any length) being aligned and non-null to distinguish /// them from other data. You can obtain a pointer that is usable as `data` @@ -148,12 +146,12 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * `data` must be [valid] for both reads and writes for `len * mem::size_of::()` many bytes, +/// * `data` must be non-null, [valid] for both reads and writes for `len * mem::size_of::()` many bytes, /// and it must be properly aligned. This means in particular: /// /// * The entire memory range of this slice must be contained within a single allocated object! /// Slices can never span across multiple allocated objects. -/// * `data` must be non-null and aligned even for zero-length slices. One +/// * `data` must be non-null and aligned even for zero-length slices or slices of ZSTs. One /// reason for this is that enum layout optimizations may rely on references /// (including slices of any length) being aligned and non-null to distinguish /// them from other data. You can obtain a pointer that is usable as `data` @@ -221,7 +219,7 @@ pub const fn from_mut(s: &mut T) -> &mut [T] { /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * The `start` pointer of the range must be a [valid] and properly aligned pointer +/// * The `start` pointer of the range must be a non-null, [valid] and properly aligned pointer /// to the first element of a slice. /// /// * The `end` pointer must be a [valid] and properly aligned pointer to *one past* @@ -237,7 +235,7 @@ pub const fn from_mut(s: &mut T) -> &mut [T] { /// of lifetime `'a`, except inside an `UnsafeCell`. /// /// * The total length of the range must be no larger than `isize::MAX`, -/// and adding that size to `data` must not "wrap around" the address space. +/// and adding that size to `start` must not "wrap around" the address space. /// See the safety documentation of [`pointer::offset`]. /// /// Note that a range created from [`slice::as_ptr_range`] fulfills these requirements. @@ -290,7 +288,7 @@ pub const unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] { /// /// Behavior is undefined if any of the following conditions are violated: /// -/// * The `start` pointer of the range must be a [valid] and properly aligned pointer +/// * The `start` pointer of the range must be a non-null, [valid] and properly aligned pointer /// to the first element of a slice. /// /// * The `end` pointer must be a [valid] and properly aligned pointer to *one past* @@ -307,7 +305,7 @@ pub const unsafe fn from_ptr_range<'a, T>(range: Range<*const T>) -> &'a [T] { /// Both read and write accesses are forbidden. /// /// * The total length of the range must be no larger than `isize::MAX`, -/// and adding that size to `data` must not "wrap around" the address space. +/// and adding that size to `start` must not "wrap around" the address space. /// See the safety documentation of [`pointer::offset`]. /// /// Note that a range created from [`slice::as_mut_ptr_range`] fulfills these requirements. diff --git a/library/core/src/slice/rotate.rs b/library/core/src/slice/rotate.rs index 1d7b86339799b..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 diff --git a/library/core/src/slice/sort/select.rs b/library/core/src/slice/sort/select.rs index 6212def30416b..f6529f23bcb3f 100644 --- a/library/core/src/slice/sort/select.rs +++ b/library/core/src/slice/sort/select.rs @@ -7,12 +7,11 @@ //! better performance than one would get using heapsort as fallback. use crate::mem::{self, SizedTypeProperties}; - 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; -/// Reorder the slice such that the element at `index` is at its final sorted position. +/// 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, diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 5111ed8756bf1..fae628a7c1474 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -1,11 +1,8 @@ //! This module contains a variety of sort implementations that are optimized for small lengths. -use crate::intrinsics; use crate::mem::{self, ManuallyDrop, MaybeUninit}; -use crate::ptr; -use crate::slice; - 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 @@ -834,18 +831,33 @@ unsafe fn bidirectional_merge bool>( 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. + // We now should have consumed the full input exactly once. This can only fail if the + // user-provided comparison function fails to implement a strict weak ordering. In that case + // we panic and never access the inconsistent state in dst. if left != left_end || right != right_end { panic_on_ord_violation(); } } } -#[inline(never)] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] +#[cfg_attr(feature = "panic_immediate_abort", inline)] fn panic_on_ord_violation() -> ! { - panic!("Ord violation"); + // This is indicative of a logic bug in the user-provided comparison function or Ord + // implementation. They are expected to implement a total order as explained in the Ord + // documentation. + // + // By panicking we inform the user, that they have a logic bug in their program. If a strict + // weak ordering is not given, the concept of comparison based sorting cannot yield a sorted + // result. E.g.: a < b < c < a + // + // The Ord documentation requires users to implement a total order. Arguably that's + // unnecessarily strict in the context of sorting. Issues only arise if the weaker requirement + // of a strict weak ordering is violated. + // + // The panic message talks about a total order because that's what the Ord documentation talks + // about and requires, so as to not confuse users. + panic!("user-provided comparison function does not correctly implement a total order"); } #[must_use] diff --git a/library/core/src/slice/sort/stable/drift.rs b/library/core/src/slice/sort/stable/drift.rs index 2d9c4ac9fcf7c..644e75a4581e9 100644 --- a/library/core/src/slice/sort/stable/drift.rs +++ b/library/core/src/slice/sort/stable/drift.rs @@ -1,14 +1,12 @@ //! This module contains the hybrid top-level loop combining bottom-up Mergesort with top-down //! Quicksort. -use crate::cmp; -use crate::intrinsics; 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)) @@ -273,7 +271,7 @@ fn stable_quicksort bool>( } /// 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. +/// can always fit in a `usize` because the maximum slice length is [`isize::MAX`]. #[derive(Copy, Clone)] struct DriftsortRun(usize); diff --git a/library/core/src/slice/sort/stable/merge.rs b/library/core/src/slice/sort/stable/merge.rs index 6739e114b130a..0cb21740795b7 100644 --- a/library/core/src/slice/sort/stable/merge.rs +++ b/library/core/src/slice/sort/stable/merge.rs @@ -1,8 +1,7 @@ //! This module contains logic for performing a merge of two sorted sub-slices. -use crate::cmp; use crate::mem::MaybeUninit; -use crate::ptr; +use crate::{cmp, ptr}; /// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `scratch` as /// temporary storage, and stores the result into `v[..]`. diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index 18f7b2ac54af5..a383b0f589ccf 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -1,12 +1,10 @@ //! This module contains the entry points for `slice::sort`. -use crate::cmp; -use crate::intrinsics; 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; diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs index 181fe603d2325..3319d67ab52fa 100644 --- a/library/core/src/slice/sort/stable/quicksort.rs +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -1,12 +1,10 @@ //! This module contains a stable quicksort and partition implementation. -use crate::intrinsics; use crate::mem::{self, ManuallyDrop, MaybeUninit}; -use crate::ptr; - 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. /// @@ -196,7 +194,8 @@ struct PartitionState { impl PartitionState { /// # Safety - /// scan and scratch must point to valid disjoint buffers of length len. The + /// + /// `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. @@ -208,6 +207,7 @@ impl PartitionState { /// 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. diff --git a/library/core/src/slice/sort/unstable/heapsort.rs b/library/core/src/slice/sort/unstable/heapsort.rs index 559605ef4b6b3..27e2ad588ea09 100644 --- a/library/core/src/slice/sort/unstable/heapsort.rs +++ b/library/core/src/slice/sort/unstable/heapsort.rs @@ -1,7 +1,6 @@ //! This module contains a branchless heapsort as fallback for unstable quicksort. -use crate::intrinsics; -use crate::ptr; +use crate::{intrinsics, ptr}; /// Sorts `v` using heapsort, which guarantees *O*(*n* \* log(*n*)) worst-case. /// diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index 692c2d8f7c7ba..932e01f4401e5 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -2,14 +2,13 @@ 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. +/// Unstable sort called ipnsort by Lukas Bergdoll and Orson Peters. /// Design document: /// /// diff --git a/library/core/src/slice/sort/unstable/quicksort.rs b/library/core/src/slice/sort/unstable/quicksort.rs index 533b5b0eec767..cd53656e9b4b8 100644 --- a/library/core/src/slice/sort/unstable/quicksort.rs +++ b/library/core/src/slice/sort/unstable/quicksort.rs @@ -1,11 +1,9 @@ //! This module contains an unstable quicksort and two partition implementations. -use crate::intrinsics; use crate::mem::{self, ManuallyDrop}; -use crate::ptr; - use crate::slice::sort::shared::pivot::choose_pivot; use crate::slice::sort::shared::smallsort::UnstableSmallSortTypeImpl; +use crate::{intrinsics, ptr}; /// Sorts `v` recursively. /// diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs index 397759bd5cae7..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))`, @@ -225,7 +224,7 @@ pub const unsafe fn from_raw_parts<'a>(ptr: *const u8, len: usize) -> &'a str { 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))`, diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 19627f28e64f8..d9301a8a66ea2 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. /// @@ -244,24 +241,35 @@ impl<'a> CharIndices<'a> { /// Returns the byte position of the next character, or the length /// of the underlying string if there are no more characters. /// + /// This means that, when the iterator has not been fully consumed, + /// the returned value will match the index that will be returned + /// by the next call to [`next()`](Self::next). + /// /// # Examples /// /// ``` - /// #![feature(char_indices_offset)] /// let mut chars = "a楽".char_indices(); /// + /// // `next()` has not been called yet, so `offset()` returns the byte + /// // index of the first character of the string, which is always 0. /// assert_eq!(chars.offset(), 0); + /// // As expected, the first call to `next()` also returns 0 as index. /// assert_eq!(chars.next(), Some((0, 'a'))); /// + /// // `next()` has been called once, so `offset()` returns the byte index + /// // of the second character ... /// assert_eq!(chars.offset(), 1); + /// // ... which matches the index returned by the next call to `next()`. /// assert_eq!(chars.next(), Some((1, '楽'))); /// + /// // Once the iterator has been consumed, `offset()` returns the length + /// // in bytes of the string. /// assert_eq!(chars.offset(), 4); /// assert_eq!(chars.next(), None); /// ``` #[inline] #[must_use] - #[unstable(feature = "char_indices_offset", issue = "83871")] + #[stable(feature = "char_indices_offset", since = "1.82.0")] pub fn offset(&self) -> usize { self.front_offset } @@ -411,7 +419,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 +432,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 +492,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 +507,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 +519,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 +528,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 +545,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 +558,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 +566,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 +586,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 +597,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 +617,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 +640,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 +697,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 +734,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 +804,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 +827,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 +868,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 +891,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 +919,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 +927,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 +937,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 +956,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 +995,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 +1018,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 +1046,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 +1069,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 +1101,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 +1125,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 { @@ -1288,7 +1296,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> { @@ -1410,7 +1418,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] @@ -1420,7 +1428,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() } @@ -1428,14 +1436,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] @@ -1445,9 +1453,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 683109380439c..cf9f1bfc0eb72 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 @@ -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 @@ -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. @@ -1037,7 +1017,7 @@ impl str { 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`). @@ -1088,7 +1068,7 @@ impl str { 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] @@ -1137,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) } @@ -1174,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) } @@ -1198,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) } @@ -1249,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) } @@ -1295,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 @@ -1417,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(), @@ -1427,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. @@ -1457,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(), @@ -1467,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. @@ -1512,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. @@ -1561,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 @@ -1607,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. @@ -1662,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. @@ -1711,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) } @@ -1731,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..))) } @@ -1749,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. @@ -1789,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. @@ -1823,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 @@ -1867,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 @@ -1907,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) } @@ -2122,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; @@ -2169,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() { @@ -2202,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) } @@ -2229,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>, -