diff options
author | Andrew Walbran <qwandor@google.com> | 2023-06-01 20:45:50 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-06-01 20:45:50 +0000 |
commit | 6128694f14608a39f371cb04332203dd55021272 (patch) | |
tree | c152abda73624cb4a77225f0682f01b33a76be30 | |
parent | eef466dd058ab69a4edff26d5ba602918dc8b071 (diff) | |
parent | c89a47375198fa255d792180bb95dabb268c803f (diff) | |
download | linkme-6128694f14608a39f371cb04332203dd55021272.tar.gz |
Initial import am: 2970041c34 am: 59f583acb4 am: a1d0e5dd7b am: e423295327 am: c89a473751
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/linkme/+/2607228
Change-Id: I1b7d8c24551caff1092e22520b5e90a10f3c4045
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
40 files changed, 1451 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..58bba9e --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "a0c3cdd977aefdf6674754ca8b1dc4aa41a7f0ae" + }, + "path_in_vcs": "" +}
\ No newline at end of file diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 0000000..66826c0 --- /dev/null +++ b/.clippy.toml @@ -0,0 +1 @@ +msrv = "1.62.0" diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..7507077 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: dtolnay diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2fcd41f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,107 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + schedule: [cron: "40 1 * * *"] + +permissions: + contents: read + +env: + RUSTFLAGS: -Dwarnings + +jobs: + pre_ci: + uses: dtolnay/.github/.github/workflows/pre_ci.yml@master + + test: + name: ${{matrix.name || format('Rust {0}', matrix.rust)}} + needs: pre_ci + if: needs.pre_ci.outputs.continue + runs-on: ${{matrix.os}}-latest + strategy: + fail-fast: false + matrix: + rust: [nightly, beta, stable, 1.62.0] + os: [ubuntu] + include: + - name: macOS + os: macos + rust: nightly + - name: Windows (gnu) + os: windows + rust: nightly-x86_64-pc-windows-gnu + - name: Windows (msvc) + os: windows + rust: nightly-x86_64-pc-windows-msvc + timeout-minutes: 45 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{matrix.rust}} + - name: Enable deny(non_exhaustive_omitted_patterns) + run: echo RUSTFLAGS=${RUSTFLAGS}\ --cfg=exhaustive >> $GITHUB_ENV + if: matrix.rust == 'nightly' + - name: Enable type layout randomization + run: echo RUSTFLAGS=${RUSTFLAGS}\ -Zrandomize-layout >> $GITHUB_ENV + if: matrix.rust == 'nightly' + - run: cargo check --manifest-path tests/crate/Cargo.toml + - run: cargo test -p linkme -p linkme-impl + # windows-gnu: https://github.com/dtolnay/linkme/issues/25 + continue-on-error: ${{matrix.rust == 'nightly-x86_64-pc-windows-gnu'}} + - run: cargo test -p linkme -p linkme-impl --features used_linker + if: startsWith(matrix.rust, 'nightly') + continue-on-error: ${{matrix.rust == 'nightly-x86_64-pc-windows-gnu'}} + - run: cargo test -p linkme -p linkme-impl --release + continue-on-error: ${{matrix.rust == 'nightly-x86_64-pc-windows-gnu'}} + - run: cargo test -p linkme -p linkme-impl --release --features used_linker + if: startsWith(matrix.rust, 'nightly') + continue-on-error: ${{matrix.rust == 'nightly-x86_64-pc-windows-gnu'}} + + cortex: + name: Cortex-M + needs: pre_ci + if: needs.pre_ci.outputs.continue + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + with: + target: thumbv7m-none-eabi + - name: Install QEMU + run: | + sudo apt-get update + sudo apt-get install -y qemu-system-arm + - run: cargo run --release + env: + RUSTFLAGS: -C link-arg=-Tlink.x -D warnings + working-directory: tests/cortex + continue-on-error: true + - run: cargo run --release --features used_linker + env: + RUSTFLAGS: -C link-arg=-Tlink.x -D warnings + working-directory: tests/cortex + + clippy: + name: Clippy + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + timeout-minutes: 45 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@clippy + - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic + + outdated: + name: Outdated + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + timeout-minutes: 45 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/install@cargo-outdated + - run: cargo outdated --workspace --exit-code 1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f88dba --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock
\ No newline at end of file diff --git a/Android.bp b/Android.bp new file mode 100644 index 0000000..86d24bf --- /dev/null +++ b/Android.bp @@ -0,0 +1,21 @@ +// This file is generated by cargo2android.py --config cargo2android.json. +// Do not modify this file as changes will be overridden on upgrade. + + + +rust_library { + name: "liblinkme", + host_supported: true, + crate_name: "linkme", + cargo_env_compat: true, + cargo_pkg_version: "0.3.9", + srcs: ["src/lib.rs"], + edition: "2021", + proc_macros: ["liblinkme_impl"], + apex_available: [ + "//apex_available:platform", + "//apex_available:anyapex", + ], + product_available: true, + vendor_available: true, +} diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d9cf97f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,54 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.62" +name = "linkme" +version = "0.3.9" +authors = ["David Tolnay <dtolnay@gmail.com>"] +description = "Safe cross-platform linker shenanigans" +documentation = "https://docs.rs/linkme" +readme = "README.md" +keywords = ["linkage"] +categories = [ + "development-tools::build-utils", + "development-tools::procedural-macro-helpers", + "no-std", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/dtolnay/linkme" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[lib] +doc-scrape-examples = false + +[[test]] +name = "module_2015" +edition = "2015" + +[dependencies.linkme-impl] +version = "=0.3.9" + +[dev-dependencies.once_cell] +version = "1.16" + +[dev-dependencies.rustversion] +version = "1.0" + +[dev-dependencies.trybuild] +version = "1.0.66" +features = ["diff"] + +[features] +used_linker = ["linkme-impl/used_linker"] diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..0c21208 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,36 @@ +[package] +name = "linkme" +version = "0.3.9" +authors = ["David Tolnay <dtolnay@gmail.com>"] +categories = ["development-tools::build-utils", "development-tools::procedural-macro-helpers", "no-std"] +description = "Safe cross-platform linker shenanigans" +documentation = "https://docs.rs/linkme" +edition = "2021" +keywords = ["linkage"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/dtolnay/linkme" +rust-version = "1.62" + +[[test]] +name = "module_2015" +edition = "2015" + +[features] +used_linker = ["linkme-impl/used_linker"] + +[dependencies] +linkme-impl = { version = "=0.3.9", path = "impl" } + +[dev-dependencies] +once_cell = "1.16" +rustversion = "1.0" +trybuild = { version = "1.0.66", features = ["diff"] } + +[workspace] +members = ["impl", "tests/cortex", "tests/crate"] + +[lib] +doc-scrape-examples = false + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] @@ -0,0 +1 @@ +LICENSE-APACHE
\ No newline at end of file diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/METADATA b/METADATA new file mode 100644 index 0000000..06d2359 --- /dev/null +++ b/METADATA @@ -0,0 +1,20 @@ +name: "linkme" +description: "Safe cross-platform linker shenanigans" +third_party { + url { + type: HOMEPAGE + value: "https://crates.io/crates/linkme" + } + url { + type: ARCHIVE + value: "https://static.crates.io/crates/linkme/linkme-0.3.9.crate" + } + version: "0.3.9" + # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same. + license_type: NOTICE + last_upgrade_date { + year: 2023 + month: 5 + day: 9 + } +} diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/MODULE_LICENSE_APACHE2 @@ -0,0 +1 @@ +include platform/prebuilts/rust:master:/OWNERS diff --git a/README.md b/README.md new file mode 100644 index 0000000..de15c56 --- /dev/null +++ b/README.md @@ -0,0 +1,151 @@ +## Linkme: safe cross-platform linker shenanigans + +[<img alt="github" src="https://img.shields.io/badge/github-dtolnay/linkme-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/linkme) +[<img alt="crates.io" src="https://img.shields.io/crates/v/linkme.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/linkme) +[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-linkme-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/linkme) +[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/dtolnay/linkme/ci.yml?branch=master&style=for-the-badge" height="20">](https://github.com/dtolnay/linkme/actions?query=branch%3Amaster) + +| Component | Linux | macOS | Windows | FreeBSD | illumos | Other...<sup>†</sup> | +|:---|:---:|:---:|:---:|:---:|:---:|:---:| +| [Distributed slice] | 💚 | 💚 | 💚 | 💚 | 💚 | | + +<b><sup>†</sup></b> We welcome PRs adding support for any platforms not listed +here. + +[Distributed slice]: #distributed-slice + +```toml +[dependencies] +linkme = "0.3" +``` + +*Supports rustc 1.62+* + +<br> + +# Distributed slice + +A distributed slice is a collection of static elements that are gathered into a +contiguous section of the binary by the linker. Slice elements may be defined +individually from anywhere in the dependency graph of the final binary. + +The implementation is based on `link_section` attributes and platform-specific +linker support. It does not involve life-before-main or any other runtime +initialization on any platform. This is a zero-cost safe abstraction that +operates entirely during compilation and linking. + +### Declaration + +A static distributed slice is declared by writing `#[distributed_slice]` on a +static item whose type is `[T]` for some type `T`. The initializer expression +must be `[..]` to indicate that elements come from elsewhere. + +```rust +use linkme::distributed_slice; + +#[distributed_slice] +pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; +``` + +### Elements + +Slice elements may be registered into a distributed slice by a +`#[distributed_slice(...)]` attribute in which the path to the distributed slice +is given in the parentheses. The initializer is required to be a const +expression. + +```rust +use linkme::distributed_slice; +use other_crate::BENCHMARKS; + +#[distributed_slice(BENCHMARKS)] +static BENCH_DESERIALIZE: fn(&mut Bencher) = bench_deserialize; + +fn bench_deserialize(b: &mut Bencher) { + /* ... */ +} +``` + +Elements may be defined in the same crate that declares the distributed slice, +or in any downstream crate. Elements across all crates linked into the final +binary will be observed to be present in the slice at runtime. + +The distributed slice behaves in all ways like `&'static [T]`. + +```rust +fn main() { + // Iterate the elements. + for bench in BENCHMARKS { + /* ... */ + } + + // Index into the elements. + let first = BENCHMARKS[0]; + + // Slice the elements. + let except_first = &BENCHMARKS[1..]; + + // Invoke methods on the underlying slice. + let len = BENCHMARKS.len(); +} +``` + +The compiler will require that the static element type matches with the element +type of the distributed slice. If the two do not match, the program will not +compile: + +```rust +#[distributed_slice(BENCHMARKS)] +static BENCH_WTF: usize = 999; +``` + +```console +error[E0308]: mismatched types + --> src/distributed_slice.rs:65:19 + | +17 | static BENCH_WTF: usize = 999; + | ^^^^^ expected fn pointer, found `usize` + | + = note: expected fn pointer `fn(&mut other_crate::Bencher)` + found type `usize` +``` + +### Function elements + +As a shorthand for the common case of distributed slices containing function +pointers, the distributed\_slice attribute may be applied directly to a function +definition to place a pointer to that function into a distributed slice. + +```rust +use linkme::distributed_slice; + +#[distributed_slice] +pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; + +// Equivalent to: +// +// #[distributed_slice(BENCHMARKS)] +// static _: fn(&mut Bencher) = bench_deserialize; +// +#[distributed_slice(BENCHMARKS)] +fn bench_deserialize(b: &mut Bencher) { + /* ... */ +} +``` + +<br> + +#### License + +<sup> +Licensed under either of <a href="LICENSE-APACHE">Apache License, Version +2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option. +</sup> + +<br> + +<sub> +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. +</sub> diff --git a/cargo2android.json b/cargo2android.json new file mode 100644 index 0000000..3224ff7 --- /dev/null +++ b/cargo2android.json @@ -0,0 +1,6 @@ +{ + "dependencies": true, + "device": true, + "run": true, + "test": true +}
\ No newline at end of file diff --git a/patches/std.diff b/patches/std.diff new file mode 100644 index 0000000..a852d47 --- /dev/null +++ b/patches/std.diff @@ -0,0 +1,15 @@ +diff --git a/src/lib.rs b/src/lib.rs +index 396057c..d460938 100644 +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -117,6 +117,10 @@ + clippy::unused_self + )] + ++// ANDROID: Use std to allow building as a dylib. ++#[cfg(android_dylib)] ++extern crate std; ++ + mod distributed_slice; + + // Not public API. diff --git a/src/distributed_slice.rs b/src/distributed_slice.rs new file mode 100644 index 0000000..707e043 --- /dev/null +++ b/src/distributed_slice.rs @@ -0,0 +1,294 @@ +use core::hint; +use core::mem; +use core::ops::Deref; +use core::slice; + +use crate::__private::Slice; + +/// Collection of static elements that are gathered into a contiguous section of +/// the binary by the linker. +/// +/// The implementation is based on `link_section` attributes and +/// platform-specific linker support. It does not involve life-before-main or +/// any other runtime initialization on any platform. This is a zero-cost safe +/// abstraction that operates entirely during compilation and linking. +/// +/// ## Declaration +/// +/// A static distributed slice may be declared by writing `#[distributed_slice]` +/// on a static item whose type is `[T]` for some type `T`. The initializer +/// expression must be `[..]` to indicate that elements come from elsewhere. +/// +/// ``` +/// # #![cfg_attr(feature = "used_linker", feature(used_with_arg))] +/// # +/// # struct Bencher; +/// # +/// use linkme::distributed_slice; +/// +/// #[distributed_slice] +/// pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; +/// ``` +/// +/// The attribute rewrites the `[T]` type of the static into +/// `DistributedSlice<[T]>`, so the static in the example technically has type +/// `DistributedSlice<[fn(&mut Bencher)]>`. +/// +/// ## Elements +/// +/// Slice elements may be registered into a distributed slice by a +/// `#[distributed_slice(...)]` attribute in which the path to the distributed +/// slice is given in the parentheses. The initializer is required to be a const +/// expression. +/// +/// Elements may be defined in the same crate that declares the distributed +/// slice, or in any downstream crate. Elements across all crates linked into +/// the final binary will be observed to be present in the slice at runtime. +/// +/// ``` +/// # #![cfg_attr(feature = "used_linker", feature(used_with_arg))] +/// # +/// # mod other_crate { +/// # use linkme::distributed_slice; +/// # +/// # pub struct Bencher; +/// # +/// # #[distributed_slice] +/// # pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; +/// # } +/// # +/// # use other_crate::Bencher; +/// # +/// use linkme::distributed_slice; +/// use other_crate::BENCHMARKS; +/// +/// #[distributed_slice(BENCHMARKS)] +/// static BENCH_DESERIALIZE: fn(&mut Bencher) = bench_deserialize; +/// +/// fn bench_deserialize(b: &mut Bencher) { +/// /* ... */ +/// } +/// ``` +/// +/// The compiler will require that the static element type matches with the +/// element type of the distributed slice. If the two do not match, the program +/// will not compile. +/// +/// ```compile_fail +/// # mod other_crate { +/// # use linkme::distributed_slice; +/// # +/// # pub struct Bencher; +/// # +/// # #[distributed_slice] +/// # pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; +/// # } +/// # +/// # use linkme::distributed_slice; +/// # use other_crate::BENCHMARKS; +/// # +/// #[distributed_slice(BENCHMARKS)] +/// static BENCH_WTF: usize = 999; +/// ``` +/// +/// ```text +/// error[E0308]: mismatched types +/// --> src/distributed_slice.rs:65:19 +/// | +/// 17 | static BENCH_WTF: usize = 999; +/// | ^^^^^ expected fn pointer, found `usize` +/// | +/// = note: expected fn pointer `fn(&mut other_crate::Bencher)` +/// found type `usize` +/// ``` +/// +/// ## Function elements +/// +/// As a shorthand for the common case of distributed slices containing function +/// pointers, the distributed\_slice attribute may be applied directly to a +/// function definition to place a pointer to that function into a distributed +/// slice. +/// +/// ``` +/// # #![cfg_attr(feature = "used_linker", feature(used_with_arg))] +/// # +/// # pub struct Bencher; +/// # +/// use linkme::distributed_slice; +/// +/// #[distributed_slice] +/// pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; +/// +/// // Equivalent to: +/// // +/// // #[distributed_slice(BENCHMARKS)] +/// // static _: fn(&mut Bencher) = bench_deserialize; +/// // +/// #[distributed_slice(BENCHMARKS)] +/// fn bench_deserialize(b: &mut Bencher) { +/// /* ... */ +/// } +/// ``` +pub struct DistributedSlice<T: ?Sized + Slice> { + name: &'static str, + section_start: StaticPtr<T::Element>, + section_stop: StaticPtr<T::Element>, + dupcheck_start: StaticPtr<usize>, + dupcheck_stop: StaticPtr<usize>, +} + +struct StaticPtr<T> { + ptr: *const T, +} + +unsafe impl<T> Send for StaticPtr<T> {} + +unsafe impl<T> Sync for StaticPtr<T> {} + +impl<T> Copy for StaticPtr<T> {} + +impl<T> Clone for StaticPtr<T> { + fn clone(&self) -> Self { + *self + } +} + +impl<T> DistributedSlice<[T]> { + #[doc(hidden)] + #[cfg(any( + target_os = "none", + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "illumos", + target_os = "freebsd" + ))] + pub const unsafe fn private_new( + name: &'static str, + section_start: *const T, + section_stop: *const T, + dupcheck_start: *const usize, + dupcheck_stop: *const usize, + ) -> Self { + DistributedSlice { + name, + section_start: StaticPtr { ptr: section_start }, + section_stop: StaticPtr { ptr: section_stop }, + dupcheck_start: StaticPtr { + ptr: dupcheck_start, + }, + dupcheck_stop: StaticPtr { ptr: dupcheck_stop }, + } + } + + #[doc(hidden)] + #[cfg(target_os = "windows")] + pub const unsafe fn private_new( + name: &'static str, + section_start: *const [T; 0], + section_stop: *const [T; 0], + dupcheck_start: *const (), + dupcheck_stop: *const (), + ) -> Self { + DistributedSlice { + name, + section_start: StaticPtr { + ptr: section_start as *const T, + }, + section_stop: StaticPtr { + ptr: section_stop as *const T, + }, + dupcheck_start: StaticPtr { + ptr: dupcheck_start as *const usize, + }, + dupcheck_stop: StaticPtr { + ptr: dupcheck_stop as *const usize, + }, + } + } + + #[doc(hidden)] + #[inline] + pub unsafe fn private_typecheck(self, element: T) { + mem::forget(element); + } +} + +impl<T> DistributedSlice<[T]> { + /// Retrieve a contiguous slice containing all the elements linked into this + /// program. + /// + /// **Note**: Ordinarily this method should not need to be called because + /// `DistributedSlice<[T]>` already behaves like `&'static [T]` in most ways + /// through the power of `Deref`. In particular, iteration and indexing and + /// method calls can all happen directly on the static without calling + /// `static_slice()`. + /// + /// ```no_run + /// # #![cfg_attr(feature = "used_linker", feature(used_with_arg))] + /// # + /// # struct Bencher; + /// # + /// use linkme::distributed_slice; + /// + /// #[distributed_slice] + /// static BENCHMARKS: [fn(&mut Bencher)] = [..]; + /// + /// fn main() { + /// // Iterate the elements. + /// for bench in BENCHMARKS { + /// /* ... */ + /// } + /// + /// // Index into the elements. + /// let first = BENCHMARKS[0]; + /// + /// // Slice the elements. + /// let except_first = &BENCHMARKS[1..]; + /// + /// // Invoke methods on the underlying slice. + /// let len = BENCHMARKS.len(); + /// } + /// ``` + pub fn static_slice(self) -> &'static [T] { + if self.dupcheck_start.ptr.wrapping_add(1) < self.dupcheck_stop.ptr { + panic!("duplicate #[distributed_slice] with name \"{}\"", self.name); + } + + let stride = mem::size_of::<T>(); + let start = self.section_start.ptr; + let stop = self.section_stop.ptr; + let byte_offset = stop as usize - start as usize; + let len = match byte_offset.checked_div(stride) { + Some(len) => len, + // The #[distributed_slice] call checks `size_of::<T>() > 0` before + // using the unsafe `private_new`. + None => unsafe { hint::unreachable_unchecked() }, + }; + unsafe { slice::from_raw_parts(start, len) } + } +} + +impl<T> Copy for DistributedSlice<[T]> {} + +impl<T> Clone for DistributedSlice<[T]> { + fn clone(&self) -> Self { + *self + } +} + +impl<T: 'static> Deref for DistributedSlice<[T]> { + type Target = [T]; + fn deref(&self) -> &'static Self::Target { + self.static_slice() + } +} + +impl<T: 'static> IntoIterator for DistributedSlice<[T]> { + type Item = &'static T; + type IntoIter = slice::Iter<'static, T>; + fn into_iter(self) -> Self::IntoIter { + self.static_slice().iter() + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d460938 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,133 @@ +//! [![github]](https://github.com/dtolnay/linkme) [![crates-io]](https://crates.io/crates/linkme) [![docs-rs]](https://docs.rs/linkme) +//! +//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github +//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust +//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs +//! +//! <br> +//! +//! **A library for safe cross-platform linker shenanigans.** +//! +//! <br> +//! +//! # Platform support +//! +//! | Component | Linux | macOS | Windows | FreeBSD | illumos | Other...<sup>†</sup> | +//! |:---|:---:|:---:|:---:|:---:|:---:|:---:| +//! | Distributed slice | 💚 | 💚 | 💚 | 💚 | 💚 | | +//! +//! <br>***<sup>†</sup>*** We welcome PRs adding support for any platforms not +//! listed here. +//! +//! <br> +//! +//! # Distributed slice +//! +//! A distributed slice is a collection of static elements that are gathered +//! into a contiguous section of the binary by the linker. Slice elements may be +//! defined individually from anywhere in the dependency graph of the final +//! binary. +//! +//! Refer to [`linkme::DistributedSlice`][DistributedSlice] for complete details +//! of the API. The basic idea is as follows. +//! +//! A static distributed slice is declared by writing `#[distributed_slice]` on +//! a static item whose type is `[T]` for some type `T`. The initializer +//! expression must be `[..]` to indicate that elements come from elsewhere. +//! +//! ``` +//! # #![cfg_attr(feature = "used_linker", feature(used_with_arg))] +//! # +//! # struct Bencher; +//! # +//! use linkme::distributed_slice; +//! +//! #[distributed_slice] +//! pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; +//! ``` +//! +//! Slice elements may be registered into a distributed slice by a +//! `#[distributed_slice(...)]` attribute in which the path to the distributed +//! slice is given in the parentheses. The initializer is required to be a const +//! expression. +//! +//! ``` +//! # #![cfg_attr(feature = "used_linker", feature(used_with_arg))] +//! # +//! # mod other_crate { +//! # use linkme::distributed_slice; +//! # +//! # pub struct Bencher; +//! # +//! # #[distributed_slice] +//! # pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; +//! # } +//! # +//! # use other_crate::Bencher; +//! # +//! use linkme::distributed_slice; +//! use other_crate::BENCHMARKS; +//! +//! #[distributed_slice(BENCHMARKS)] +//! static BENCH_DESERIALIZE: fn(&mut Bencher) = bench_deserialize; +//! +//! fn bench_deserialize(b: &mut Bencher) { +//! /* ... */ +//! } +//! ``` +//! +//! The distributed slice behaves in all ways like `&'static [T]`. +//! +//! ```no_run +//! # #![cfg_attr(feature = "used_linker", feature(used_with_arg))] +//! # +//! # use linkme::distributed_slice; +//! # +//! # struct Bencher; +//! # +//! # #[distributed_slice] +//! # static BENCHMARKS: [fn(&mut Bencher)] = [..]; +//! # +//! fn main() { +//! // Iterate the elements. +//! for bench in BENCHMARKS { +//! /* ... */ +//! } +//! +//! // Index into the elements. +//! let first = BENCHMARKS[0]; +//! +//! // Slice the elements. +//! let except_first = &BENCHMARKS[1..]; +//! +//! // Invoke methods on the underlying slice. +//! let len = BENCHMARKS.len(); +//! } +//! ``` + +#![no_std] +#![doc(html_root_url = "https://docs.rs/linkme/0.3.9")] +#![allow( + clippy::doc_markdown, + clippy::empty_enum, + clippy::expl_impl_clone_on_copy, + clippy::manual_assert, + clippy::missing_panics_doc, + clippy::must_use_candidate, + clippy::unused_self +)] + +// ANDROID: Use std to allow building as a dylib. +#[cfg(android_dylib)] +extern crate std; + +mod distributed_slice; + +// Not public API. +#[doc(hidden)] +#[path = "private.rs"] +pub mod __private; + +pub use linkme_impl::*; + +pub use crate::distributed_slice::DistributedSlice; diff --git a/src/private.rs b/src/private.rs new file mode 100644 index 0000000..508ef4f --- /dev/null +++ b/src/private.rs @@ -0,0 +1,17 @@ +pub use core::assert; +pub use core::mem; +pub use core::primitive::usize; + +pub trait Slice { + type Element; +} + +impl<T> Slice for [T] { + type Element = T; +} + +pub enum Void {} + +pub fn value<T>() -> T { + panic!() +} diff --git a/tests/compiletest.rs b/tests/compiletest.rs new file mode 100644 index 0000000..7974a62 --- /dev/null +++ b/tests/compiletest.rs @@ -0,0 +1,7 @@ +#[rustversion::attr(not(nightly), ignore)] +#[cfg_attr(miri, ignore)] +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); +} diff --git a/tests/custom_linkme_path.rs b/tests/custom_linkme_path.rs new file mode 100644 index 0000000..daee74f --- /dev/null +++ b/tests/custom_linkme_path.rs @@ -0,0 +1,37 @@ +#![cfg_attr(feature = "used_linker", feature(used_with_arg))] + +use linkme as link_me; + +mod declaration { + use crate::link_me::distributed_slice; + + #[distributed_slice] + #[linkme(crate = crate::link_me)] + pub static SLICE: [i32] = [..]; + + #[test] + fn test_slice() { + assert!(!SLICE.is_empty()); + } + + #[distributed_slice] + #[linkme(crate = crate::link_me)] + pub static FUNCTIONS: [fn()] = [..]; + + #[test] + fn test_functions() { + assert!(!FUNCTIONS.is_empty()); + } +} + +mod usage { + use crate::link_me::distributed_slice; + + #[distributed_slice(super::declaration::SLICE)] + #[linkme(crate = crate::link_me)] + pub static N: i32 = 9; + + #[distributed_slice(super::declaration::FUNCTIONS)] + #[linkme(crate = crate::link_me)] + fn test_me() {} +} diff --git a/tests/distributed_slice.rs b/tests/distributed_slice.rs new file mode 100644 index 0000000..f8ab487 --- /dev/null +++ b/tests/distributed_slice.rs @@ -0,0 +1,72 @@ +#![cfg_attr(feature = "used_linker", feature(used_with_arg))] + +use linkme::distributed_slice; +use once_cell::sync::Lazy; + +#[distributed_slice] +static SHENANIGANS: [i32] = [..]; + +#[distributed_slice(SHENANIGANS)] +static N: i32 = 9; + +#[distributed_slice(SHENANIGANS)] +static NN: i32 = 99; + +#[distributed_slice(SHENANIGANS)] +static NNN: i32 = 999; + +#[test] +fn test() { + assert_eq!(SHENANIGANS.len(), 3); + + let mut sum = 0; + for n in SHENANIGANS { + sum += n; + } + + assert_eq!(sum, 9 + 99 + 999); +} + +#[test] +fn test_empty() { + #[distributed_slice] + static EMPTY: [i32] = [..]; + + assert!(EMPTY.is_empty()); +} + +#[test] +fn test_non_copy() { + struct NonCopy(i32); + + #[distributed_slice] + static NONCOPY: [NonCopy] = [..]; + + #[distributed_slice(NONCOPY)] + static ELEMENT: NonCopy = NonCopy(9); + + assert!(!NONCOPY.is_empty()); +} + +#[test] +fn test_interior_mutable() { + #[distributed_slice] + static MUTABLE: [Lazy<i32>] = [..]; + + #[distributed_slice(MUTABLE)] + static ELEMENT: Lazy<i32> = Lazy::new(|| -1); + + assert!(MUTABLE.len() == 1); + assert!(*MUTABLE[0] == -1); +} + +#[test] +fn test_elided_lifetime() { + #[distributed_slice] + pub static MYSLICE: [&str] = [..]; + + #[distributed_slice(MYSLICE)] + static ELEMENT: &str = "..."; + + assert!(!MYSLICE.is_empty()); +} diff --git a/tests/example.rs b/tests/example.rs new file mode 100644 index 0000000..bbe439b --- /dev/null +++ b/tests/example.rs @@ -0,0 +1,32 @@ +#![cfg_attr(feature = "used_linker", feature(used_with_arg))] +#![deny(warnings)] +#![allow(clippy::no_effect_underscore_binding)] + +use linkme::distributed_slice; + +pub struct Bencher; + +#[distributed_slice] +pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; + +#[distributed_slice(BENCHMARKS)] +static BENCH_DESERIALIZE: fn(&mut Bencher) = bench_deserialize; + +fn bench_deserialize(_b: &mut Bencher) { + /* ... */ +} + +#[test] +fn readme() { + // Iterate the elements. + for _bench in BENCHMARKS { /* ... */ } + + // Index into the elements. + let _first = BENCHMARKS[0]; + + // Slice the elements. + let _except_first = &BENCHMARKS[1..]; + + // Invoke methods on the underlying slice. + let _len = BENCHMARKS.len(); +} diff --git a/tests/fn_element.rs b/tests/fn_element.rs new file mode 100644 index 0000000..a80508f --- /dev/null +++ b/tests/fn_element.rs @@ -0,0 +1,31 @@ +#![cfg_attr(feature = "used_linker", feature(used_with_arg))] +#![allow(clippy::needless_lifetimes, clippy::trivially_copy_pass_by_ref)] + +use linkme::distributed_slice; + +#[distributed_slice] +pub static SLICE1: [fn()] = [..]; + +#[distributed_slice(SLICE1)] +fn foo() {} + +#[distributed_slice] +pub static SLICE2: [for<'a, 'b> fn(&'a &'b ())] = [..]; + +#[distributed_slice(SLICE2)] +fn bar<'a, 'b>(_: &'a &'b ()) {} + +#[distributed_slice] +pub static SLICE3: [unsafe extern "C" fn() -> i32] = [..]; + +#[distributed_slice(SLICE3)] +unsafe extern "C" fn baz() -> i32 { + 42 +} + +#[test] +fn test_slices() { + assert!(!SLICE1.is_empty()); + assert!(!SLICE2.is_empty()); + assert!(!SLICE3.is_empty()); +} diff --git a/tests/module/mod.rs b/tests/module/mod.rs new file mode 100644 index 0000000..0fe3e5b --- /dev/null +++ b/tests/module/mod.rs @@ -0,0 +1,18 @@ +mod declaration { + use linkme::distributed_slice; + + #[distributed_slice] + pub static SLICE: [i32] = [..]; + + #[test] + fn test_mod_slice() { + assert!(!SLICE.is_empty()); + } +} + +mod usage { + use linkme::distributed_slice; + + #[distributed_slice(super::declaration::SLICE)] + pub static N: i32 = 9; +} diff --git a/tests/module_2015.rs b/tests/module_2015.rs new file mode 100644 index 0000000..1a5d049 --- /dev/null +++ b/tests/module_2015.rs @@ -0,0 +1,5 @@ +#![cfg_attr(feature = "used_linker", feature(used_with_arg))] + +extern crate linkme; + +include!("module/mod.rs"); diff --git a/tests/module_2021.rs b/tests/module_2021.rs new file mode 100644 index 0000000..b7f1e0a --- /dev/null +++ b/tests/module_2021.rs @@ -0,0 +1,3 @@ +#![cfg_attr(feature = "used_linker", feature(used_with_arg))] + +include!("module/mod.rs"); diff --git a/tests/ui/bad_crate_path.rs b/tests/ui/bad_crate_path.rs new file mode 100644 index 0000000..ce2e563 --- /dev/null +++ b/tests/ui/bad_crate_path.rs @@ -0,0 +1,20 @@ +#![cfg_attr(feature = "used_linker", feature(used_with_arg))] + +use linkme::distributed_slice; + +mod path { + pub mod to {} +} + +#[distributed_slice] +#[linkme(crate = path::to::missing)] +pub static SLICE1: [&'static str] = [..]; + +#[distributed_slice] +pub static SLICE2: [&'static str] = [..]; + +#[distributed_slice(SLICE2)] +#[linkme(crate = path::to::missing)] +static ELEMENT: &str = ""; + +fn main() {} diff --git a/tests/ui/bad_crate_path.stderr b/tests/ui/bad_crate_path.stderr new file mode 100644 index 0000000..5ac1d6a --- /dev/null +++ b/tests/ui/bad_crate_path.stderr @@ -0,0 +1,33 @@ +error[E0433]: failed to resolve: could not find `missing` in `to` + --> tests/ui/bad_crate_path.rs:10:28 + | +10 | #[linkme(crate = path::to::missing)] + | ^^^^^^^ could not find `missing` in `to` + +error[E0433]: failed to resolve: could not find `missing` in `to` + --> tests/ui/bad_crate_path.rs:17:28 + | +17 | #[linkme(crate = path::to::missing)] + | ^^^^^^^ could not find `missing` in `to` + +error[E0433]: failed to resolve: could not find `missing` in `to` + --> tests/ui/bad_crate_path.rs:10:28 + | +10 | #[linkme(crate = path::to::missing)] + | ^^^^^^^ could not find `missing` in `to` + | +help: consider importing this struct + | +3 | use linkme::DistributedSlice; + | + +error[E0433]: failed to resolve: could not find `missing` in `to` + --> tests/ui/bad_crate_path.rs:17:28 + | +17 | #[linkme(crate = path::to::missing)] + | ^^^^^^^ could not find `missing` in `to` + | +help: consider importing this struct + | +3 | use linkme::DistributedSlice; + | diff --git a/tests/ui/generic_fn.rs b/tests/ui/generic_fn.rs new file mode 100644 index 0000000..cb9be03 --- /dev/null +++ b/tests/ui/generic_fn.rs @@ -0,0 +1,14 @@ +#![cfg_attr(feature = "used_linker", feature(used_with_arg))] + +use linkme::distributed_slice; + +#[distributed_slice] +pub static SLICES: [fn()] = [..]; + +#[distributed_slice(SLICES)] +fn type_param<T>() {} + +#[distributed_slice(SLICES)] +fn const_param<const N: usize>() {} + +fn main() {} diff --git a/tests/ui/generic_fn.stderr b/tests/ui/generic_fn.stderr new file mode 100644 index 0000000..2730c8d --- /dev/null +++ b/tests/ui/generic_fn.stderr @@ -0,0 +1,11 @@ +error: cannot have generic parameters on distributed slice element + --> tests/ui/generic_fn.rs:9:15 + | +9 | fn type_param<T>() {} + | ^ + +error: cannot have generic parameters on distributed slice element + --> tests/ui/generic_fn.rs:12:16 + | +12 | fn const_param<const N: usize>() {} + | ^^^^^^^^^^^^^^ diff --git a/tests/ui/mismatched_types.rs b/tests/ui/mismatched_types.rs new file mode 100644 index 0000000..611c8a9 --- /dev/null +++ b/tests/ui/mismatched_types.rs @@ -0,0 +1,16 @@ +#![cfg_attr(feature = "used_linker", feature(used_with_arg))] + +use linkme::distributed_slice; + +pub struct Bencher; + +#[distributed_slice] +pub static BENCHMARKS: [fn(&mut Bencher)] = [..]; + +#[distributed_slice(BENCHMARKS)] +static BENCH_WTF: usize = 999; + +#[distributed_slice(BENCHMARKS)] +fn wrong_bench_fn<'a>(_: &'a mut ()) {} + +fn main() {} diff --git a/tests/ui/mismatched_types.stderr b/tests/ui/mismatched_types.stderr new file mode 100644 index 0000000..643d1a1 --- /dev/null +++ b/tests/ui/mismatched_types.stderr @@ -0,0 +1,31 @@ +error[E0308]: mismatched types + --> tests/ui/mismatched_types.rs:11:19 + | +10 | #[distributed_slice(BENCHMARKS)] + | -------------------------------- arguments to this function are incorrect +11 | static BENCH_WTF: usize = 999; + | ^^^^^ expected fn pointer, found `usize` + | + = note: expected fn pointer `for<'a> fn(&'a mut Bencher)` + found type `usize` +note: method defined here + --> src/distributed_slice.rs + | + | pub unsafe fn private_typecheck(self, element: T) { + | ^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> tests/ui/mismatched_types.rs:14:1 + | +13 | #[distributed_slice(BENCHMARKS)] + | -------------------------------- arguments to this function are incorrect +14 | fn wrong_bench_fn<'a>(_: &'a mut ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Bencher`, found `()` + | + = note: expected fn pointer `for<'a> fn(&'a mut Bencher)` + found fn pointer `for<'a> fn(&'a mut ())` +note: method defined here + --> src/distributed_slice.rs + | + | pub unsafe fn private_typecheck(self, element: T) { + | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/mutable.rs b/tests/ui/mutable.rs new file mode 100644 index 0000000..39db0d0 --- /dev/null +++ b/tests/ui/mutable.rs @@ -0,0 +1,11 @@ +#![cfg_attr(feature = "used_linker", feature(used_with_arg))] + +use linkme::distributed_slice; + +#[distributed_slice] +pub static mut SLICE: [i32] = [..]; + +#[distributed_slice(BENCHMARKS)] +static mut ELEMENT: i32 = -1; + +fn main() {} diff --git a/tests/ui/mutable.stderr b/tests/ui/mutable.stderr new file mode 100644 index 0000000..c23e194 --- /dev/null +++ b/tests/ui/mutable.stderr @@ -0,0 +1,11 @@ +error: static mut is not supported by distributed_slice + --> tests/ui/mutable.rs:6:12 + | +6 | pub static mut SLICE: [i32] = [..]; + | ^^^ + +error: static mut is not supported by distributed_slice + --> tests/ui/mutable.rs:9:8 + | +9 | static mut ELEMENT: i32 = -1; + | ^^^ diff --git a/tests/ui/unsupported_item.rs b/tests/ui/unsupported_item.rs new file mode 100644 index 0000000..e218ce5 --- /dev/null +++ b/tests/ui/unsupported_item.rs @@ -0,0 +1,11 @@ +#![cfg_attr(feature = "used_linker", feature(used_with_arg))] + +use linkme::distributed_slice; + +#[distributed_slice] +pub static SLICE: [&'static str] = [..]; + +#[distributed_slice(SLICE)] +extern crate std as _std; + +fn main() {} diff --git a/tests/ui/unsupported_item.stderr b/tests/ui/unsupported_item.stderr new file mode 100644 index 0000000..b0eae07 --- /dev/null +++ b/tests/ui/unsupported_item.stderr @@ -0,0 +1,5 @@ +error: distributed element must be either static or function item + --> tests/ui/unsupported_item.rs:9:1 + | +9 | extern crate std as _std; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/zerosized.rs b/tests/ui/zerosized.rs new file mode 100644 index 0000000..3965cee --- /dev/null +++ b/tests/ui/zerosized.rs @@ -0,0 +1,10 @@ +#![cfg_attr(feature = "used_linker", feature(used_with_arg))] + +use linkme::distributed_slice; + +pub struct Unit; + +#[distributed_slice] +pub static ZEROSIZED: [Unit] = [..]; + +fn main() {} diff --git a/tests/ui/zerosized.stderr b/tests/ui/zerosized.stderr new file mode 100644 index 0000000..2443140 --- /dev/null +++ b/tests/ui/zerosized.stderr @@ -0,0 +1,7 @@ +error[E0080]: could not evaluate static initializer + --> tests/ui/zerosized.rs:7:1 + | +7 | #[distributed_slice] + | ^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: ::linkme::__private::mem::size_of::<<[Unit] as\n ::linkme::__private::Slice>::Element>() > 0', $DIR/tests/ui/zerosized.rs:7:1 + | + = note: this error originates in the macro `::linkme::__private::assert` (in Nightly builds, run with -Z macro-backtrace for more info) |